Merge branch 'main' of dev.v4.games:v4games/v4k
commit
7dfc8a2bf8
3
MAKE.bat
3
MAKE.bat
|
@ -158,6 +158,9 @@ if "%1"=="pull" (
|
|||
)
|
||||
|
||||
if "%1"=="depot" (
|
||||
pushd depot\
|
||||
git pull
|
||||
popd
|
||||
git submodule update --remote --merge depot/
|
||||
exit /b
|
||||
)
|
||||
|
|
2
_mirror
2
_mirror
|
@ -1 +1 @@
|
|||
Subproject commit a239c188cadce8af6d28ae7402184cd7a3073fc2
|
||||
Subproject commit f7cf64ed703b21981c5c5e600bd55c43e0b9e66e
|
191
bind/v4k.lua
191
bind/v4k.lua
|
@ -1143,15 +1143,6 @@ ffi.cdef([[
|
|||
//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 font_highlight(const char *text, const void *colors);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 font_highlight(const char *text, const void *colors);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 font_highlight(const char *text, const void *colors);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 atof2(const char *s);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 atof2(const char *s);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 atof2(const char *s);
|
||||
//lcpp INF [0000] vec3: macro name but used as C declaration in:API vec3 atof3(const char *s);
|
||||
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC vec3 atof3(const char *s);
|
||||
//lcpp INF [0000] vec3: macro name but used as C declaration in: vec3 atof3(const char *s);
|
||||
//lcpp INF [0000] vec4: macro name but used as C declaration in:API vec4 atof4(const char *s);
|
||||
//lcpp INF [0000] vec4: macro name but used as C declaration in:STATIC vec4 atof4(const char *s);
|
||||
//lcpp INF [0000] vec4: macro name but used as C declaration in: vec4 atof4(const char *s);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:API char* ftoa2(vec2 v);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC char* ftoa2(vec2 v);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in: char* ftoa2(vec2 v);
|
||||
|
@ -1161,6 +1152,27 @@ ffi.cdef([[
|
|||
//lcpp INF [0000] vec4: macro name but used as C declaration in:API char* ftoa4(vec4 v);
|
||||
//lcpp INF [0000] vec4: macro name but used as C declaration in:STATIC char* ftoa4(vec4 v);
|
||||
//lcpp INF [0000] vec4: macro name but used as C declaration in: char* ftoa4(vec4 v);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:API vec2 atof2(const char *s);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC vec2 atof2(const char *s);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in: vec2 atof2(const char *s);
|
||||
//lcpp INF [0000] vec3: macro name but used as C declaration in:API vec3 atof3(const char *s);
|
||||
//lcpp INF [0000] vec3: macro name but used as C declaration in:STATIC vec3 atof3(const char *s);
|
||||
//lcpp INF [0000] vec3: macro name but used as C declaration in: vec3 atof3(const char *s);
|
||||
//lcpp INF [0000] vec4: macro name but used as C declaration in:API vec4 atof4(const char *s);
|
||||
//lcpp INF [0000] vec4: macro name but used as C declaration in:STATIC vec4 atof4(const char *s);
|
||||
//lcpp INF [0000] vec4: macro name but used as C declaration in: vec4 atof4(const char *s);
|
||||
//lcpp INF [0000] vec2i: macro name but used as C declaration in:API char* itoa2(vec2i v);
|
||||
//lcpp INF [0000] vec2i: macro name but used as C declaration in:STATIC char* itoa2(vec2i v);
|
||||
//lcpp INF [0000] vec2i: macro name but used as C declaration in: char* itoa2(vec2i v);
|
||||
//lcpp INF [0000] vec3i: macro name but used as C declaration in:API char* itoa3(vec3i v);
|
||||
//lcpp INF [0000] vec3i: macro name but used as C declaration in:STATIC char* itoa3(vec3i v);
|
||||
//lcpp INF [0000] vec3i: macro name but used as C declaration in: char* itoa3(vec3i v);
|
||||
//lcpp INF [0000] vec2i: macro name but used as C declaration in:API vec2i atoi2(const char *s);
|
||||
//lcpp INF [0000] vec2i: macro name but used as C declaration in:STATIC vec2i atoi2(const char *s);
|
||||
//lcpp INF [0000] vec2i: macro name but used as C declaration in: vec2i atoi2(const char *s);
|
||||
//lcpp INF [0000] vec3i: macro name but used as C declaration in:API vec3i atoi3(const char *s);
|
||||
//lcpp INF [0000] vec3i: macro name but used as C declaration in:STATIC vec3i atoi3(const char *s);
|
||||
//lcpp INF [0000] vec3i: macro name but used as C declaration in: vec3i atoi3(const char *s);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:API void swapf2(vec2 *a, vec2 *b);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:API void swapf2(vec2 *a, vec2 *b);
|
||||
//lcpp INF [0000] vec2: macro name but used as C declaration in:STATIC void swapf2(vec2 *a, vec2 *b);
|
||||
|
@ -1641,9 +1653,10 @@ EASE_IN,
|
|||
EASE_INOUT = EASE_IN * 2,
|
||||
EASE_OUT = 0,
|
||||
};
|
||||
float ease(float t01, unsigned mode);
|
||||
float ease_ping_pong(float t, float(*fn1)(float), float(*fn2)(float));
|
||||
float ease_pong_ping(float t, float(*fn1)(float), float(*fn2)(float));
|
||||
float ease(float t01, unsigned fn);
|
||||
float ease_pong(float t01, unsigned fn);
|
||||
float ease_ping_pong(float t, unsigned fn1, unsigned fn2);
|
||||
float ease_pong_ping(float t, unsigned fn1, unsigned fn2);
|
||||
float deg (float radians);
|
||||
float rad (float degrees);
|
||||
int mini (int a, int b);
|
||||
|
@ -2190,6 +2203,23 @@ uintptr_t id_make(void *ptr);
|
|||
void * id_handle(uintptr_t id);
|
||||
void id_dispose(uintptr_t id);
|
||||
bool id_valid(uintptr_t id);
|
||||
int semver( int major, int minor, int patch );
|
||||
int semvercmp( int v1, int v2 );
|
||||
typedef struct byte2 { uint8_t x,y; } byte2;
|
||||
typedef struct byte3 { uint8_t x,y,z; } byte3;
|
||||
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
|
||||
typedef struct int2 { int x,y; } int2;
|
||||
typedef struct int3 { int x,y,z; } int3;
|
||||
typedef struct int4 { int x,y,z,w; } int4;
|
||||
typedef struct uint2 { unsigned int x,y; } uint2;
|
||||
typedef struct uint3 { unsigned int x,y,z; } uint3;
|
||||
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
|
||||
typedef struct float2 { float x,y; } float2;
|
||||
typedef struct float3 { float x,y,z; } float3;
|
||||
typedef struct float4 { float x,y,z,w; } float4;
|
||||
typedef struct double2 { double x,y; } double2;
|
||||
typedef struct double3 { double x,y,z; } double3;
|
||||
typedef struct double4 { double x,y,z,w; } double4;
|
||||
char *cc4str(unsigned cc);
|
||||
char *cc8str(uint64_t cc);
|
||||
enum {
|
||||
|
@ -2197,13 +2227,20 @@ cc__1 = '1', cc__2, cc__3, cc__4, cc__5, cc__6,cc__7, cc__8, cc__9, cc__0, cc___
|
|||
cc__A = 'A', cc__B, cc__C, cc__D, cc__E, cc__F,cc__G, cc__H, cc__I, cc__J, cc__K,cc__L, cc__M, cc__N, cc__O, cc__P,cc__Q, cc__R, cc__S, cc__T, cc__U,cc__V, cc__W, cc__X, cc__Y, cc__Z,
|
||||
cc__a = 'a', cc__b, cc__c, cc__d, cc__e, cc__f,cc__g, cc__h, cc__i, cc__j, cc__k,cc__l, cc__m, cc__n, cc__o, cc__p,cc__q, cc__r, cc__s, cc__t, cc__u,cc__v, cc__w, cc__x, cc__y, cc__z,
|
||||
};
|
||||
vec2 atof2(const char *s);
|
||||
vec3 atof3(const char *s);
|
||||
vec4 atof4(const char *s);
|
||||
char* ftoa(float f);
|
||||
char* ftoa1(float v);
|
||||
char* ftoa2(vec2 v);
|
||||
char* ftoa3(vec3 v);
|
||||
char* ftoa4(vec4 v);
|
||||
float atof1(const char *s);
|
||||
vec2 atof2(const char *s);
|
||||
vec3 atof3(const char *s);
|
||||
vec4 atof4(const char *s);
|
||||
char* itoa1(int v);
|
||||
char* itoa2(vec2i v);
|
||||
char* itoa3(vec3i v);
|
||||
int atoi1(const char *s);
|
||||
vec2i atoi2(const char *s);
|
||||
vec3i atoi3(const char *s);
|
||||
int is_big();
|
||||
int is_little();
|
||||
uint16_t swap16( uint16_t x );
|
||||
|
@ -2416,23 +2453,95 @@ enum { NETWORK_USERID = 7, NETWORK_COUNT , NETWORK_CAPACITY };
|
|||
void server_send_bin(int64_t handle, const void *ptr, int len);
|
||||
void server_drop(int64_t handle);
|
||||
int64_t client_join(const char *ip, int port);
|
||||
int semver( int major, int minor, int patch );
|
||||
int semvercmp( int v1, int v2 );
|
||||
typedef struct byte2 { uint8_t x,y; } byte2;
|
||||
typedef struct byte3 { uint8_t x,y,z; } byte3;
|
||||
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
|
||||
typedef struct int2 { int x,y; } int2;
|
||||
typedef struct int3 { int x,y,z; } int3;
|
||||
typedef struct int4 { int x,y,z,w; } int4;
|
||||
typedef struct uint2 { unsigned int x,y; } uint2;
|
||||
typedef struct uint3 { unsigned int x,y,z; } uint3;
|
||||
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
|
||||
typedef struct float2 { float x,y; } float2;
|
||||
typedef struct float3 { float x,y,z; } float3;
|
||||
typedef struct float4 { float x,y,z,w; } float4;
|
||||
typedef struct double2 { double x,y; } double2;
|
||||
typedef struct double3 { double x,y,z; } double3;
|
||||
typedef struct double4 { double x,y,z,w; } double4;
|
||||
typedef struct obj { struct { union { uintptr_t objheader; struct { uintptr_t objtype:8; uintptr_t objheap:1; uintptr_t objsizew:7; uintptr_t objrefs:8; uintptr_t objcomps:1; uintptr_t objnameid:16; uintptr_t objid:16+3; uintptr_t objunused:64-8-7-1-1-8-16-16-3; }; }; }; } obj;
|
||||
typedef struct entity { struct { union { uintptr_t objheader; struct { uintptr_t objtype:8; uintptr_t objheap:1; uintptr_t objsizew:7; uintptr_t objrefs:8; uintptr_t objcomps:1; uintptr_t objnameid:16; uintptr_t objid:16+3; uintptr_t objunused:64-8-7-1-1-8-16-16-3; }; }; union { struct { uintptr_t objenabled:32, objflagged:32; }; uintptr_t cflags; }; void *c[32]; }; } entity;
|
||||
obj *objtmp;
|
||||
void* obj_malloc(unsigned sz);
|
||||
void* obj_free(void *o);
|
||||
extern void (*obj_ctor[256])();
|
||||
extern void (*obj_dtor[256])();
|
||||
extern char* (*obj_save[256])();
|
||||
extern bool (*obj_load[256])();
|
||||
extern int (*obj_test[256])();
|
||||
extern int (*obj_init[256])();
|
||||
extern int (*obj_quit[256])();
|
||||
extern int (*obj_tick[256])();
|
||||
extern int (*obj_draw[256])();
|
||||
extern int (*obj_lerp[256])();
|
||||
extern int (*obj_edit[256])();
|
||||
uintptr_t obj_header(const void *o);
|
||||
uintptr_t obj_id(const void *o);
|
||||
const char* obj_name(const void *o);
|
||||
unsigned obj_typeid(const void *o);
|
||||
const char* obj_type(const void *o);
|
||||
int obj_sizeof(const void *o);
|
||||
int obj_size(const void *o);
|
||||
char* obj_data(void *o);
|
||||
const char* obj_datac(const void *o);
|
||||
void* obj_payload(const void *o);
|
||||
void* obj_zero(void *o);
|
||||
void* obj_ref(void *oo);
|
||||
void* obj_unref(void *oo);
|
||||
obj* obj_detach(void *c);
|
||||
obj* obj_attach(void *o, void *c);
|
||||
obj* obj_root(const void *o);
|
||||
obj* obj_parent(const void *o);
|
||||
obj***obj_children(const void *o);
|
||||
obj***obj_siblings(const void *o);
|
||||
int obj_dumptree(const void *o);
|
||||
const char* obj_metaset(const void *o, const char *key, const char *value);
|
||||
const char* obj_metaget(const void *o, const char *key);
|
||||
void* obj_swap(void *dst, void *src);
|
||||
void* obj_copy_fast(void *dst, const void *src);
|
||||
void* obj_copy(void *dst, const void *src);
|
||||
int obj_comp_fast(const void *a, const void *b);
|
||||
int obj_comp(const void *a, const void *b);
|
||||
int obj_lesser(const void *a, const void *b);
|
||||
int obj_greater(const void *a, const void *b);
|
||||
int obj_equal(const void *a, const void *b);
|
||||
uint64_t obj_hash(const void *o);
|
||||
bool obj_hexdump(const void *oo);
|
||||
int obj_print(const void *o);
|
||||
int obj_printf(const void *o, const char *text);
|
||||
int obj_console(const void *o);
|
||||
char* obj_saveini(const void *o);
|
||||
obj* obj_mergeini(void *o, const char *ini);
|
||||
obj* obj_loadini(void *o, const char *ini);
|
||||
char* obj_savejson(const void *o);
|
||||
obj* obj_mergejson(void *o, const char *json);
|
||||
obj* obj_loadjson(void *o, const char *json);
|
||||
char* obj_savebin(const void *o);
|
||||
obj* obj_mergebin(void *o, const char *sav);
|
||||
obj* obj_loadbin(void *o, const char *sav);
|
||||
char* obj_savempack(const void *o);
|
||||
obj* obj_mergempack(void *o, const char *sav);
|
||||
obj* obj_loadmpack(void *o, const char *sav);
|
||||
int obj_push(const void *o);
|
||||
int obj_pop(void *o);
|
||||
bool obj_addcomponent(void *object, unsigned c, void *ptr);
|
||||
bool obj_hascomponent(void *object, unsigned c);
|
||||
void* obj_getcomponent(void *object, unsigned c);
|
||||
bool obj_delcomponent(void *object, unsigned c);
|
||||
bool obj_usecomponent(void *object, unsigned c);
|
||||
bool obj_offcomponent(void *object, unsigned c);
|
||||
char* entity_save(entity *self);
|
||||
void* obj_clone(const void *src);
|
||||
void* obj_merge(void *dst, const void *src);
|
||||
void* obj_mutate(void **dst, const void *src);
|
||||
void* obj_make(const char *str);
|
||||
typedef enum OBJTYPE_BUILTINS {
|
||||
OBJTYPE_obj = 0,
|
||||
OBJTYPE_entity = 1,
|
||||
OBJTYPE_vec2 = 2,
|
||||
OBJTYPE_vec3 = 3,
|
||||
OBJTYPE_vec4 = 4,
|
||||
OBJTYPE_quat = 5,
|
||||
OBJTYPE_mat33 = 6,
|
||||
OBJTYPE_mat34 = 7,
|
||||
OBJTYPE_mat44 = 8,
|
||||
OBJTYPE_vec2i = 9,
|
||||
OBJTYPE_vec3i = 10,
|
||||
} OBJTYPE_BUILTINS;
|
||||
int profiler_enable(bool on);
|
||||
struct profile_t { double stat; int32_t cost, avg; };
|
||||
typedef struct { map base; struct { pair p; char * key; struct profile_t val; } tmp, *ptr; struct profile_t* tmpval; int (*typed_cmp)(char *, char *); uint64_t (*typed_hash)(char *); } * profiler_t;
|
||||
|
@ -2456,12 +2565,12 @@ unsigned bytes;
|
|||
void * function_find(const char *F);
|
||||
reflect_t member_find(const char *T, const char *M);
|
||||
void * member_findptr(void *obj, const char *T, const char *M);
|
||||
reflect_t* members_find(const char *T);
|
||||
void type_inscribe(const char *TY,unsigned TYid,unsigned TYsz,const char *infos);
|
||||
void enum_inscribe(const char *E,unsigned Eid,unsigned Eval,const char *infos);
|
||||
void struct_inscribe(const char *T,unsigned Tid,unsigned Tsz,unsigned OBJTYPEid, const char *infos);
|
||||
void member_inscribe(unsigned Tid, const char *M,unsigned Mid,unsigned Msz, const char *infos, const char *type, unsigned bytes);
|
||||
void function_inscribe(const char *F,unsigned Fid,void *func,const char *infos);
|
||||
reflect_t** members_find(const char *T);
|
||||
void type_inscribe(const char *TY,unsigned TYsz,const char *infos);
|
||||
void enum_inscribe(const char *E,unsigned Eval,const char *infos);
|
||||
void struct_inscribe(const char *T,unsigned Tsz,unsigned OBJTYPEid, const char *infos);
|
||||
void member_inscribe(const char *T, const char *M,unsigned Msz, const char *infos, const char *type, unsigned bytes);
|
||||
void function_inscribe(const char *F,void *func,const char *infos);
|
||||
void reflect_print(const char *symbol);
|
||||
void reflect_dump(const char *mask);
|
||||
void reflect_init();
|
||||
|
@ -2470,7 +2579,7 @@ typedef unsigned handle;
|
|||
unsigned bgra( uint8_t b, uint8_t g, uint8_t r, uint8_t a );
|
||||
unsigned rgbaf( float r, float g, float b, float a );
|
||||
unsigned bgraf( float b, float g, float r, float a );
|
||||
float alpha( unsigned rgba );
|
||||
unsigned alpha( unsigned rgba );
|
||||
enum IMAGE_FLAGS {
|
||||
IMAGE_R =4096,
|
||||
IMAGE_RG =8192,
|
||||
|
@ -3090,7 +3199,7 @@ typedef vec3i guid;
|
|||
void alert(const char *message);
|
||||
void hexdump( const void *ptr, unsigned len );
|
||||
void hexdumpf( FILE *fp, const void *ptr, unsigned len, int width );
|
||||
void breakpoint(const char *optional_reason);
|
||||
void breakpoint();
|
||||
bool has_debugger();
|
||||
void trap_install(void);
|
||||
const char *trap_name(int signal);
|
||||
|
|
2
depot
2
depot
|
@ -1 +1 @@
|
|||
Subproject commit 660eaa0992e99ed1a36c50faf7f4f5f2c84ad2c0
|
||||
Subproject commit ea89db8ecd2b32fca2a21f67f486601de5c6a93c
|
2084
engine/joint/v4k.h
2084
engine/joint/v4k.h
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,490 @@
|
|||
/* A mathematical expression evaluator.
|
||||
* It uses a recursive descent parser internally.
|
||||
* Author: Werner Stoop
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
* http://unlicense.org/
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h> /* remember to compile with -lm */
|
||||
#include <setjmp.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Special tokens used by the lexer function lex()
|
||||
* they've been chosen as non-printable characters
|
||||
* so that printable characters can be used for other
|
||||
* purposes
|
||||
*/
|
||||
#define TOK_END 0 /* end of text */
|
||||
#define TOK_INI 1 /* Initial state */
|
||||
#define TOK_ID 2 /* identifier */
|
||||
#define TOK_NUM 3 /* number */
|
||||
|
||||
/* Types of errors */
|
||||
// 0 /* "no error" */
|
||||
#define ERR_MEMORY 1 /* "out of memory" */
|
||||
#define ERR_LEXER 2 /* "unknown token" */
|
||||
#define ERR_LONGID 3 /* "identifier too long" */
|
||||
#define ERR_VALUE 4 /* "value expected" */
|
||||
#define ERR_BRACKET 5 /* "missing ')'" */
|
||||
#define ERR_FUNC 6 /* "unknown function" */
|
||||
#define ERR_ARGS 7 /* "wrong number of arguments" */
|
||||
#define ERR_CONST 8 /* "unknown constant" */
|
||||
|
||||
/* Other definitions */
|
||||
#define MAX_ID_LEN 11 /* Max length of an identifier */
|
||||
#define OPERATORS "+-*/%(),^" /* Valid operators */
|
||||
|
||||
#define EVAL_PI 3.141592654
|
||||
#define EVAL_E 2.718281828
|
||||
#define EVAL_DEG (EVAL_PI/180)
|
||||
|
||||
/* Internal structure for the parser/evaluator */
|
||||
struct eval {
|
||||
|
||||
jmp_buf j; /* For error handling */
|
||||
|
||||
const char *p; /* Position in the text being parsed */
|
||||
|
||||
double *st; /* Stack */
|
||||
int st_size; /* Stack size */
|
||||
int sp; /* Stack pointer */
|
||||
|
||||
/* The current and next tokens identified by the lexer */
|
||||
struct {
|
||||
int type; /* Type of the token */
|
||||
double n_val; /* Numeric value of the previous lexed token */
|
||||
char s_val[MAX_ID_LEN]; /* String (identifier) value of the previous lexed token */
|
||||
} token[2];
|
||||
|
||||
int cur_tok; /* Current token, either 0 or 1 (see the comments of lex()) */
|
||||
};
|
||||
|
||||
/* Prototypes */
|
||||
static double pop(struct eval *ev);
|
||||
static void push(struct eval *ev, double d);
|
||||
static int lex(struct eval *ev);
|
||||
|
||||
/* Prototypes for the recursive descent parser */
|
||||
static void expr(struct eval *ev);
|
||||
static void add_expr(struct eval *ev);
|
||||
static void mul_expr(struct eval *ev);
|
||||
static void pow_expr(struct eval *ev);
|
||||
static void uni_expr(struct eval *ev);
|
||||
static void bra_expr(struct eval *ev);
|
||||
static void id_expr(struct eval *ev);
|
||||
static void num_expr(struct eval *ev);
|
||||
|
||||
/*
|
||||
* Evaluates a mathemeatical expression
|
||||
*/
|
||||
double eval(const char *exp/*, int *ep*/) {
|
||||
int _ep, *ep = &_ep;
|
||||
struct eval ev;
|
||||
double ans = 0.0;
|
||||
|
||||
assert(ep != NULL);
|
||||
|
||||
/* Allocate a stack */
|
||||
ev.st_size = 10;
|
||||
ev.st = CALLOC(ev.st_size, sizeof *ev.st);
|
||||
if(!ev.st)
|
||||
{
|
||||
*ep = ERR_MEMORY;
|
||||
return NAN; //0.0;
|
||||
}
|
||||
ev.sp = 0;
|
||||
|
||||
/* Manage errors */
|
||||
*ep = setjmp(ev.j);
|
||||
if(*ep != 0)
|
||||
{
|
||||
FREE(ev.st);
|
||||
return NAN; //0.0;
|
||||
}
|
||||
|
||||
/* Initialize the lexer */
|
||||
ev.token[0].type = TOK_INI;
|
||||
ev.token[0].s_val[0] = '\0';
|
||||
ev.token[1].type = TOK_INI;
|
||||
ev.token[1].s_val[0] = '\0';
|
||||
ev.cur_tok = 0;
|
||||
|
||||
/* Initialize the parser */
|
||||
ev.p = exp;
|
||||
|
||||
/* lex once to initialize the lexer */
|
||||
if(lex(&ev) != TOK_END)
|
||||
{
|
||||
expr(&ev);
|
||||
ans = pop(&ev);
|
||||
}
|
||||
|
||||
FREE(ev.st);
|
||||
return ans;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pushes a value onto the stack, increases the stack size if necessary
|
||||
*/
|
||||
static void push(struct eval *ev, double d) {
|
||||
if(ev->sp == ev->st_size) {
|
||||
/* Resize the stack by 1.5 */
|
||||
double *old = ev->st;
|
||||
int new_size = ev->st_size + (ev->st_size >> 1);
|
||||
ev->st = REALLOC(ev->st, new_size);
|
||||
if(!ev->st) {
|
||||
ev->st = old;
|
||||
longjmp(ev->j, ERR_MEMORY);
|
||||
}
|
||||
|
||||
ev->st_size = new_size;
|
||||
}
|
||||
|
||||
ev->st[ev->sp++] = d;
|
||||
}
|
||||
|
||||
// Pops a value from the top of the stack
|
||||
static double pop(struct eval *ev) {
|
||||
assert(ev->sp > 0);
|
||||
return ev->st[--ev->sp];
|
||||
}
|
||||
|
||||
// stricmp() is common, but not standard, so I provide my own
|
||||
static int istrcmp(const char *p, const char *q) {
|
||||
for(; tolower(p[0]) == tolower(q[0]) && p[0]; p++, q++);
|
||||
return tolower(p[0]) - tolower(q[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lexical analyzer function
|
||||
*
|
||||
* In order to implement LL(1), struct eval has an array of two token structures,
|
||||
* and its cur_tok member is used to point to the _current_ token, while the other
|
||||
* element contains the _next_ token. This implements a 2 element ring buffer where
|
||||
* the lexer always writes to the _next_ token so that the recursive descent parser can
|
||||
* _peek_ at the next token.
|
||||
*/
|
||||
|
||||
static int lex(struct eval *ev) {
|
||||
int next_tok;
|
||||
|
||||
start:
|
||||
/* Cycle the tokens */
|
||||
next_tok = ev->cur_tok;
|
||||
ev->cur_tok = ev->cur_tok?0:1;
|
||||
|
||||
while(isspace(ev->p[0])) ev->p++;
|
||||
|
||||
if(!ev->p[0]) {
|
||||
/* End of the expression */
|
||||
ev->token[next_tok].type = TOK_END;
|
||||
goto end;
|
||||
}
|
||||
else if(isdigit(ev->p[0]) || ev->p[0] == '.') {
|
||||
/* Number */
|
||||
char *endp;
|
||||
ev->token[next_tok].type = TOK_NUM;
|
||||
ev->token[next_tok].n_val = strtod(ev->p, &endp);
|
||||
ev->p = endp;
|
||||
goto end;
|
||||
}
|
||||
else if(isalpha(ev->p[0])) {
|
||||
/* Identifier */
|
||||
int i;
|
||||
for(i = 0; isalnum(ev->p[0]) && i < MAX_ID_LEN - 1; i++, ev->p++)
|
||||
ev->token[next_tok].s_val[i] = ev->p[0];
|
||||
|
||||
if(isalpha(ev->p[0])) longjmp(ev->j, ERR_LONGID);
|
||||
|
||||
ev->token[next_tok].s_val[i] = '\0';
|
||||
ev->token[next_tok].type = TOK_ID;
|
||||
goto end;
|
||||
}
|
||||
else if(strchr(OPERATORS, ev->p[0])) {
|
||||
/* Operator */
|
||||
ev->token[next_tok].type = ev->p[0];
|
||||
ev->p++;
|
||||
goto end;
|
||||
}
|
||||
else /* Unknown token */
|
||||
longjmp(ev->j, ERR_LEXER);
|
||||
|
||||
end:
|
||||
|
||||
/* If this was the first call, cycle the tokens again */
|
||||
if(ev->token[ev->cur_tok].type == TOK_INI)
|
||||
goto start;
|
||||
|
||||
return ev->token[ev->cur_tok].type;
|
||||
}
|
||||
|
||||
#define EVAL_TYPE(e) (e->token[e->cur_tok].type)
|
||||
#define EVAL_ERROR(c) longjmp(ev->j, (c))
|
||||
|
||||
// num_expr ::= NUMBER
|
||||
static void num_expr(struct eval *ev) {
|
||||
if(EVAL_TYPE(ev) != TOK_NUM)
|
||||
EVAL_ERROR(ERR_VALUE);
|
||||
push(ev, ev->token[ev->cur_tok].n_val);
|
||||
lex(ev);
|
||||
}
|
||||
|
||||
// expr ::= add_expr
|
||||
static void expr(struct eval *ev) {
|
||||
add_expr(ev);
|
||||
}
|
||||
|
||||
// add_expr ::= mul_expr [('+'|'-') mul_expr]
|
||||
static void add_expr(struct eval *ev) {
|
||||
int t;
|
||||
mul_expr(ev);
|
||||
while((t =EVAL_TYPE(ev)) == '+' || t == '-') {
|
||||
double a,b;
|
||||
lex(ev);
|
||||
mul_expr(ev);
|
||||
b = pop(ev);
|
||||
a = pop(ev);
|
||||
|
||||
if(t == '+')
|
||||
push(ev, a + b);
|
||||
else
|
||||
push(ev, a - b);
|
||||
}
|
||||
}
|
||||
|
||||
// mul_expr ::= pow_expr [('*'|'/'|'%') pow_expr]
|
||||
static void mul_expr(struct eval *ev) {
|
||||
int t;
|
||||
pow_expr(ev);
|
||||
while((t = EVAL_TYPE(ev)) == '*' || t == '/' || t == '%') {
|
||||
double a,b;
|
||||
lex(ev);
|
||||
pow_expr(ev);
|
||||
b = pop(ev);
|
||||
a = pop(ev);
|
||||
|
||||
if(t == '*')
|
||||
push(ev, a * b);
|
||||
else if(t == '/')
|
||||
push(ev, a / b);
|
||||
else
|
||||
push(ev, fmod(a, b));
|
||||
}
|
||||
}
|
||||
|
||||
// pow_expr ::= uni_expr ['^' pow_expr]
|
||||
static void pow_expr(struct eval *ev) {
|
||||
/* Note that exponentiation is right associative:
|
||||
2^3^4 is 2^(3^4), not (2^3)^4 */
|
||||
uni_expr(ev);
|
||||
if(EVAL_TYPE(ev) == '^') {
|
||||
double a,b;
|
||||
lex(ev);
|
||||
pow_expr(ev);
|
||||
b = pop(ev);
|
||||
a = pop(ev);
|
||||
push(ev, pow(a,b));
|
||||
}
|
||||
}
|
||||
|
||||
// uni_expr ::= ['+'|'-'] bra_expr
|
||||
static void uni_expr(struct eval *ev) {
|
||||
int t = '+';
|
||||
if(EVAL_TYPE(ev) == '-' || EVAL_TYPE(ev) == '+') {
|
||||
t = EVAL_TYPE(ev);
|
||||
lex(ev);
|
||||
}
|
||||
|
||||
bra_expr(ev);
|
||||
|
||||
if(t == '-') {
|
||||
double a = pop(ev);
|
||||
push(ev, -a);
|
||||
}
|
||||
}
|
||||
|
||||
// bra_expr ::= '(' add_expr ')' | id_expr
|
||||
static void bra_expr(struct eval *ev) {
|
||||
if(EVAL_TYPE(ev) == '(') {
|
||||
lex(ev);
|
||||
add_expr(ev);
|
||||
if(EVAL_TYPE(ev) != ')')
|
||||
EVAL_ERROR(ERR_BRACKET);
|
||||
lex(ev);
|
||||
}
|
||||
else
|
||||
id_expr(ev);
|
||||
}
|
||||
|
||||
// id_expr ::= ID '(' add_expr [',' add_expr]* ')' | ID | num_expr
|
||||
static void id_expr(struct eval *ev) {
|
||||
int nargs = 0;
|
||||
char id[MAX_ID_LEN];
|
||||
if(EVAL_TYPE(ev) != TOK_ID) {
|
||||
num_expr(ev);
|
||||
} else {
|
||||
strcpy(id, ev->token[ev->cur_tok].s_val);
|
||||
lex(ev);
|
||||
if(EVAL_TYPE(ev) != '(') {
|
||||
/**/ if(!istrcmp(id, "true")) push(ev, 1.0);
|
||||
else if(!istrcmp(id, "false")) push(ev, 0.0);
|
||||
else if(!istrcmp(id, "on")) push(ev, 1.0);
|
||||
else if(!istrcmp(id, "off")) push(ev, 0.0);
|
||||
// pi - 3.141592654
|
||||
else if(!istrcmp(id, "pi"))
|
||||
push(ev, EVAL_PI);
|
||||
// e - base of natural logarithms, 2.718281828
|
||||
else if(!istrcmp(id, "e"))
|
||||
push(ev, EVAL_E);
|
||||
// deg - deg2rad, allows to degree conversion `sin(90*deg) = 1`
|
||||
else if(!istrcmp(id, "deg"))
|
||||
push(ev, EVAL_DEG);
|
||||
else
|
||||
EVAL_ERROR(ERR_CONST);
|
||||
} else {
|
||||
lex(ev);
|
||||
|
||||
while(EVAL_TYPE(ev) != ')') {
|
||||
add_expr(ev);
|
||||
nargs++;
|
||||
if(EVAL_TYPE(ev) == ')') break;
|
||||
|
||||
if(EVAL_TYPE(ev) != ',')
|
||||
EVAL_ERROR(ERR_BRACKET);
|
||||
lex(ev);
|
||||
}
|
||||
lex(ev);
|
||||
|
||||
// abs(x) - absolute value of x
|
||||
if(!istrcmp(id, "abs")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, fabs(pop(ev)));
|
||||
}
|
||||
// ceil(x) - smallest integer greater than x
|
||||
else if(!istrcmp(id, "ceil")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, ceil(pop(ev)));
|
||||
}
|
||||
// floor(x) - largest integer smaller than x
|
||||
else if(!istrcmp(id, "floor")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, floor(pop(ev)));
|
||||
}
|
||||
// sin(x) - sine of x, in radians
|
||||
else if(!istrcmp(id, "sin")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, sin(pop(ev)));
|
||||
}
|
||||
// asin(x) - arcsine of x, in radians
|
||||
else if(!istrcmp(id, "asin")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, asin(pop(ev)));
|
||||
}
|
||||
// cos(x) - cosine of x, in radians
|
||||
else if(!istrcmp(id, "cos")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, cos(pop(ev)));
|
||||
}
|
||||
// acos(x) - arccosine of x, in radians
|
||||
else if(!istrcmp(id, "acos")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, acos(pop(ev)));
|
||||
}
|
||||
// tan(x) - tangent of x, in radians
|
||||
else if(!istrcmp(id, "tan")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, tan(pop(ev)));
|
||||
}
|
||||
// atan(x) - arctangent of x, in radians
|
||||
else if(!istrcmp(id, "atan")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, atan(pop(ev)));
|
||||
}
|
||||
// atan(y,x) - arctangent of y/x, in radians.
|
||||
else if(!istrcmp(id, "atan2")) {
|
||||
double a, b;
|
||||
if(nargs != 2) EVAL_ERROR(ERR_ARGS);
|
||||
b = pop(ev);
|
||||
a = pop(ev);
|
||||
push(ev, atan2(a,b));
|
||||
}
|
||||
// sinh(x) - hyperbolic sine of x, in radians
|
||||
else if(!istrcmp(id, "sinh")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, sinh(pop(ev)));
|
||||
}
|
||||
// cosh(x) - hyperbolic cosine of x, in radians
|
||||
else if(!istrcmp(id, "cosh")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, cosh(pop(ev)));
|
||||
}
|
||||
// tanh(x) - hyperbolic tangent of x, in radians
|
||||
else if(!istrcmp(id, "tanh")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, tanh(pop(ev)));
|
||||
}
|
||||
// log(x) - natural logarithm of x
|
||||
else if(!istrcmp(id, "log")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, log(pop(ev)));
|
||||
}
|
||||
// log10(x) - logarithm of x, base-10
|
||||
else if(!istrcmp(id, "log10")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, log10(pop(ev)));
|
||||
}
|
||||
// exp(x) - computes e^x
|
||||
else if(!istrcmp(id, "exp")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, exp(pop(ev)));
|
||||
}
|
||||
// sqrt(x) - square root of x
|
||||
else if(!istrcmp(id, "sqrt")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, sqrt(pop(ev)));
|
||||
}
|
||||
// rad(x) - converts x from degrees to radians
|
||||
else if(!istrcmp(id, "rad")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, pop(ev)*EVAL_PI/180);
|
||||
}
|
||||
// deg(x) - converts x from radians to degrees
|
||||
else if(!istrcmp(id, "deg")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, pop(ev)*180/EVAL_PI);
|
||||
}
|
||||
// pow(x,y) - computes x^y
|
||||
else if(!istrcmp(id, "pow")) {
|
||||
double a, b;
|
||||
if(nargs != 2) EVAL_ERROR(ERR_ARGS);
|
||||
b = pop(ev);
|
||||
a = pop(ev);
|
||||
push(ev, pow(a,b));
|
||||
}
|
||||
// hypot(x,y) - computes sqrt(x*x + y*y)
|
||||
else if(!istrcmp(id, "hypot")) {
|
||||
double a, b;
|
||||
if(nargs != 2) EVAL_ERROR(ERR_ARGS);
|
||||
b = pop(ev);
|
||||
a = pop(ev);
|
||||
push(ev, sqrt(a*a + b*b));
|
||||
}
|
||||
else
|
||||
EVAL_ERROR(ERR_FUNC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
#ifdef EVALDEMO
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
assert( eval("1+1") == 2 ); // common path
|
||||
assert( eval("1+") != eval("1+") ); // check that errors return NAN
|
||||
assert(~puts("Ok") );
|
||||
}
|
||||
#endif
|
|
@ -8020,7 +8020,7 @@ nk_utf_decode(const char *c, nk_rune *u, int clen)
|
|||
*u = NK_UTF_INVALID;
|
||||
|
||||
udecoded = nk_utf_decode_byte(c[0], &len);
|
||||
if (!NK_BETWEEN(len, 1, NK_UTF_SIZE))
|
||||
if (!NK_BETWEEN(len, 1, NK_UTF_SIZE+1)) //< @r-lyeh: add +1 for len<=4
|
||||
return 1;
|
||||
|
||||
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
|
||||
|
@ -20155,7 +20155,12 @@ window->is_window_resizing |= layout->flags & NK_WINDOW_SCALE_TOP ? NK_WINDOW_SC
|
|||
}
|
||||
|
||||
}
|
||||
ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT];
|
||||
int icon = //< @r-lyeh
|
||||
((layout->flags & NK_WINDOW_SCALE_TOP) && !(layout->flags & NK_WINDOW_SCALE_LEFT))
|
||||
||
|
||||
((layout->flags & NK_WINDOW_SCALE_LEFT) && !(layout->flags & NK_WINDOW_SCALE_TOP))
|
||||
? NK_CURSOR_RESIZE_TOP_LEFT_DOWN_RIGHT : NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT;
|
||||
ctx->style.cursor_active = ctx->style.cursors[icon]; //< @r-lyeh
|
||||
in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = scaler.x + scaler.w/2.0f;
|
||||
in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = scaler.y + scaler.h/2.0f;
|
||||
}
|
||||
|
|
|
@ -169,6 +169,8 @@ errno_t fopen_s(
|
|||
{{FILE:3rd_xml.h}}
|
||||
#undef g
|
||||
{{FILE:3rd_polychop.h}}
|
||||
#define expr expr2 // 3rd_lua.h
|
||||
{{FILE:3rd_eval.h}}
|
||||
// #define SQLITE_OMIT_LOAD_EXTENSION
|
||||
// #define SQLITE_CORE 1
|
||||
// #define SQLITE_DEBUG 1
|
||||
|
|
|
@ -169,6 +169,7 @@
|
|||
#define conc4t(a,b) a##b ///-
|
||||
|
||||
#define macro(name) concat(name, __LINE__)
|
||||
#define unique(name) concat(concat(concat(name,concat(_L,__LINE__)),_),__COUNTER__)
|
||||
#define defer(begin,end) for(int macro(i) = ((begin), 0); !macro(i); macro(i) = ((end), 1))
|
||||
#define scope(end) defer((void)0, end)
|
||||
#define benchmark for(double macro(i) = 1, macro(t) = (time_ss(),-time_ss()); macro(i); macro(t)+=time_ss(), macro(i)=0, printf("%.4fs %2.f%% (" FILELINE ")\n", macro(t), macro(t)*100/0.0166667 ))
|
||||
|
@ -193,10 +194,10 @@
|
|||
#define ASSERT(expr, ...) (void)0
|
||||
#define ASSERT_ONCE(expr, ...) (void)0
|
||||
#else
|
||||
#define ASSERT(expr, ...) do { int fool_msvc[] = {0,}; if(!(expr)) { fool_msvc[0]++; breakpoint(va("!Expression failed: " #expr " " FILELINE "\n" __VA_ARGS__)); } } while(0)
|
||||
#define ASSERT_ONCE(expr, ...) do { int fool_msvc[] = {0,}; if(!(expr)) { fool_msvc[0]++; static int seen = 0; if(!seen) seen = 1, breakpoint(va("!Expression failed: " #expr " " FILELINE "\n" __VA_ARGS__)); } } while(0)
|
||||
#define ASSERT(expr, ...) do { int fool_msvc[] = {0,}; if(!(expr)) { fool_msvc[0]++; alert(va("!Expression failed: " #expr " " FILELINE "\n" __VA_ARGS__)), breakpoint(); } } while(0)
|
||||
#define ASSERT_ONCE(expr, ...) do { int fool_msvc[] = {0,}; if(!(expr)) { fool_msvc[0]++; static int seen = 0; if(!seen) seen = 1, alert(va("!Expression failed: " #expr " " FILELINE "\n" __VA_ARGS__)), breakpoint(); } } while(0)
|
||||
#endif
|
||||
#define STATIC_ASSERT(EXPR) typedef struct { unsigned macro(static_assert_on_line_) : !!(EXPR); } macro(static_assert_on_line_)
|
||||
#define STATIC_ASSERT(EXPR) typedef struct { unsigned macro(static_assert_on_L) : !!(EXPR); } unique(static_assert_on_L)
|
||||
|
||||
#define FILELINE __FILE__ ":" STRINGIZE(__LINE__)
|
||||
#define STRINGIZE(x) STRINGIZ3(x)
|
||||
|
@ -273,6 +274,7 @@
|
|||
// note: based on code by Joe Lowe (public domain).
|
||||
// note: XIU for C initializers, XCU for C++ initializers, XTU for C deinitializers
|
||||
|
||||
#define AUTORUN AUTORUN_( unique(fn) )
|
||||
#ifdef __cplusplus
|
||||
#define AUTORUN_(fn) \
|
||||
static void fn(void); \
|
||||
|
@ -286,14 +288,16 @@
|
|||
__declspec(allocate(".CRT$XIU")) \
|
||||
static int(* concat(fn,__2) )() = concat(fn,__1); \
|
||||
static void fn(void)
|
||||
#else // gcc,tcc,clang,clang-cl...
|
||||
#elif defined __TINYC__ // tcc...
|
||||
#define AUTORUN_(fn) \
|
||||
__attribute__((constructor)) \
|
||||
static void fn(void)
|
||||
#else // gcc,clang,clang-cl...
|
||||
#define AUTORUN_(fn) \
|
||||
__attribute__((constructor(__COUNTER__+101))) \
|
||||
static void fn(void)
|
||||
#endif
|
||||
|
||||
#define AUTORUN AUTORUN_( concat(concat(concat(fn_L,__LINE__),_),__COUNTER__) )
|
||||
|
||||
#if 0 // autorun demo
|
||||
void byebye(void) { puts("seen after main()"); }
|
||||
AUTORUN { puts("seen before main()"); }
|
||||
|
@ -310,7 +314,7 @@ AUTORUN { puts("seen before main() too"); atexit( byebye ); }
|
|||
// -----------------------------------------------------------------------------
|
||||
// visibility
|
||||
|
||||
// win32 users would need to -DAPI=IMPORT/EXPORT as needed when using/building V4K as DLL.
|
||||
// win32 users would need to -DAPI=EXPORT/IMPORT as needed when building/using V4K as DLL.
|
||||
|
||||
#define IMPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllimport)), __declspec(dllimport)))
|
||||
#define EXPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllexport)), __declspec(dllexport)))
|
||||
|
|
|
@ -74,7 +74,7 @@ static __thread unsigned array_n_;
|
|||
#define array_vlen_(t) ( vlen(t) - 0 )
|
||||
#define array_realloc_(t,n) ( (t) = array_cast(t) vrealloc((t), ((n)+0) * sizeof(0[t])) )
|
||||
#define array_free(t) array_clear(t)
|
||||
#else // new: with reserve support (bugs?)
|
||||
#else // new: with reserve support (@todo: check for bugs?)
|
||||
#define array_reserve(t, n) ( array_realloc_((t),(n)), array_clear(t) )
|
||||
#define array_clear(t) ( array_realloc_((t),0) ) // -1
|
||||
#define array_vlen_(t) ( vlen(t) - sizeof(0[t]) ) // -1
|
||||
|
@ -121,7 +121,7 @@ static __thread unsigned array_n_;
|
|||
memcpy( (t), src, array_count(src) * sizeof(0[t])); \
|
||||
} while(0)
|
||||
|
||||
#define array_erase_fast(t, i) do { /*may alter ordering*/ \
|
||||
#define array_erase_fast(t, i) do { /*alters ordering*/ \
|
||||
memcpy( &(t)[i], &(t)[array_count(t) - 1], sizeof(0[t])); \
|
||||
array_pop(t); \
|
||||
} while(0)
|
||||
|
|
|
@ -160,8 +160,8 @@ int editor_send(const char *cmd, const char *optional_value) {
|
|||
else if( !strcmp(cmd, "key_battery" )) *powersave = optional_value ? !!atoi(optional_value) : *powersave ^ 1;
|
||||
else if( !strcmp(cmd, "key_browser" )) ui_show("File Browser", ui_visible("File Browser") ^ true);
|
||||
else if( !strcmp(cmd, "key_outliner" )) ui_show("Outliner", ui_visible("Outliner") ^ true);
|
||||
else if( !strcmp(cmd, "key_record" )) if(record_active()) record_stop(); else
|
||||
name = file_counter(va("%s.mp4",app_name())), window_record(name), ui_notify(va("Video capturing: %s", name), date_string());
|
||||
else if( !strcmp(cmd, "key_record" )) if(record_active()) record_stop(), ui_notify(va("Video recorded"), date_string()); else
|
||||
app_beep(), name = file_counter(va("%s.mp4",app_name())), window_record(name);
|
||||
else if( !strcmp(cmd, "key_screenshot" )) name = file_counter(va("%s.png",app_name())), window_screenshot(name), ui_notify(va("Screenshot: %s", name), date_string());
|
||||
else if( !strcmp(cmd, "key_profiler" )) ui_show("Profiler", profiler_enable(ui_visible("Profiler") ^ true));
|
||||
else if( !strcmp(cmd, "key_fullscreen" )) record_stop(), window_fullscreen( window_has_fullscreen() ^ 1 ); // framebuffer resizing corrupts video stream, so stop any recording beforehand
|
||||
|
|
|
@ -1634,7 +1634,7 @@ void font_color(const char *tag, uint32_t color) {
|
|||
if( f->initialized ) {
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
glBindTexture(GL_TEXTURE_1D, f->texture_colors);
|
||||
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, FONT_MAX_COLORS, GL_BGRA, GL_UNSIGNED_BYTE, font_palette);
|
||||
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, FONT_MAX_COLORS, GL_RGBA, GL_UNSIGNED_BYTE, font_palette);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1832,7 +1832,7 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
|
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
// last chance to inspect the font atlases
|
||||
if( flag("--debug-font-atlas") )
|
||||
if( flag("--font-debug") )
|
||||
stbi_write_png(va("debug_font_atlas%d.png", index), f->width, f->height, 1, bitmap, 0);
|
||||
|
||||
FREE(bitmap);
|
||||
|
@ -1880,7 +1880,7 @@ void font_face_from_mem(const char *tag, const void *ttf_bufferv, unsigned ttf_l
|
|||
glGenTextures(1, &f->texture_colors);
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
glBindTexture(GL_TEXTURE_1D, f->texture_colors);
|
||||
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, FONT_MAX_COLORS, 0, GL_BGRA, GL_UNSIGNED_BYTE, font_palette);
|
||||
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, FONT_MAX_COLORS, 0, GL_RGBA, GL_UNSIGNED_BYTE, font_palette);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
|
|
|
@ -60,9 +60,6 @@ void v4k_quit(void) {
|
|||
|
||||
void v4k_init() {
|
||||
do_once {
|
||||
// abort run if any test suite failed
|
||||
if( test_errs ) exit(-1);
|
||||
|
||||
// install signal handlers
|
||||
ifdef(debug, trap_install());
|
||||
|
||||
|
|
|
@ -112,9 +112,6 @@ float ease_inout_bounce(float t) { return t < 0.5f ? 0.5f*ease_in_bounce(t*2) :
|
|||
|
||||
float ease_inout_perlin(float t) { float t3=t*t*t,t4=t3*t,t5=t4*t; return 6*t5-15*t4+10*t3; }
|
||||
|
||||
float ease_ping_pong(float t, float(*fn1)(float), float(*fn2)(float)) { return t < 0.5 ? fn1(t*2) : fn2(1-(t-0.5)*2); }
|
||||
float ease_pong_ping(float t, float(*fn1)(float), float(*fn2)(float)) { return 1 - ease_ping_pong(t,fn1,fn2); }
|
||||
|
||||
float ease(float t01, unsigned mode) {
|
||||
typedef float (*easing)(float);
|
||||
easing modes[] = {
|
||||
|
@ -159,6 +156,10 @@ float ease(float t01, unsigned mode) {
|
|||
return modes[clampi(mode, 0, countof(modes))](clampf(t01,0,1));
|
||||
}
|
||||
|
||||
float ease_pong(float t, unsigned fn) { return 1 - ease(t, fn); }
|
||||
float ease_ping_pong(float t, unsigned fn1, unsigned fn2) { return t < 0.5 ? ease(t*2,fn1) : ease(1-(t-0.5)*2,fn2); }
|
||||
float ease_pong_ping(float t, unsigned fn1, unsigned fn2) { return 1 - ease_ping_pong(t,fn1,fn2); }
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
float deg (float radians) { return radians / C_PI * 180.0f; }
|
||||
|
@ -953,3 +954,11 @@ void printq( quat q ) { print_(&q.x,4,1); }
|
|||
void print33( float *m ) { print_(m,3,3); }
|
||||
void print34( float *m ) { print_(m,3,4); }
|
||||
void print44( float *m ) { print_(m,4,4); }
|
||||
|
||||
// -----------
|
||||
|
||||
AUTORUN {
|
||||
STRUCT( vec3, float, x );
|
||||
STRUCT( vec3, float, y );
|
||||
STRUCT( vec3, float, z, "Up" );
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
// Credits: @ands+@krig+@vurtun (PD), @datenwolf (WTFPL2), @evanw+@barerose (CC0), @sgorsten (Unlicense).
|
||||
|
||||
#define C_EPSILON (1e-6)
|
||||
#define C_PI (3.141592654f) // (3.14159265358979323846f)
|
||||
#define TO_RAD (C_PI/180.f)
|
||||
#define TO_DEG (180.f/C_PI)
|
||||
#define C_PI (3.14159265358979323846f) // (3.141592654f)
|
||||
#define TO_RAD (C_PI/180)
|
||||
#define TO_DEG (180/C_PI)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -39,7 +39,6 @@ API void randset(uint64_t state);
|
|||
API uint64_t rand64(void);
|
||||
API double randf(void); // [0, 1) interval
|
||||
API int randi(int mini, int maxi); // [mini, maxi) interval
|
||||
//API double rng(void); // [0..1) Lehmer RNG "minimal standard"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -105,10 +104,10 @@ enum EASE_FLAGS {
|
|||
EASE_OUT = 0,
|
||||
};
|
||||
|
||||
API float ease(float t01, unsigned mode); // 0=linear,1=out_sine...31=inout_perlin
|
||||
|
||||
API float ease_ping_pong(float t, float(*fn1)(float), float(*fn2)(float));
|
||||
API float ease_pong_ping(float t, float(*fn1)(float), float(*fn2)(float));
|
||||
API float ease(float t01, unsigned fn); // / 0-to-1
|
||||
API float ease_pong(float t01, unsigned fn); // \ 1-to-0
|
||||
API float ease_ping_pong(float t, unsigned fn1, unsigned fn2); // /\ 0-to-1-to-0
|
||||
API float ease_pong_ping(float t, unsigned fn1, unsigned fn2); // \/ 1-to-0-to-1
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -1,44 +1,801 @@
|
|||
// -----------------------------------------------------------------------------
|
||||
// semantic versioning in a single byte (octal)
|
||||
// C objects framework
|
||||
// - rlyeh, public domain.
|
||||
//
|
||||
// - single octal byte that represents semantic versioning (major.minor.patch).
|
||||
// - allowed range [0000..0377] ( <-> [0..255] decimal )
|
||||
// - comparison checks only major.minor tuple as per convention.
|
||||
|
||||
int semver( int major, int minor, int patch ) {
|
||||
return SEMVER(major, minor, patch);
|
||||
// --- implement new vtables
|
||||
|
||||
obj_vtable(ctor, void, {} );
|
||||
obj_vtable(dtor, void, {} );
|
||||
|
||||
obj_vtable_null(save, char* );
|
||||
obj_vtable_null(load, bool );
|
||||
obj_vtable_null(test, int );
|
||||
|
||||
obj_vtable_null(init, int );
|
||||
obj_vtable_null(quit, int );
|
||||
obj_vtable_null(tick, int );
|
||||
obj_vtable_null(draw, int );
|
||||
|
||||
obj_vtable_null(lerp, int );
|
||||
obj_vtable_null(edit, int ); // OSC cmds: argc,argv "undo","redo","cut","copy","paste","edit","view","menu"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const char *OBJTYPES[256] = { 0 }; // = { REPEAT256("") };
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// heap/stack ctor/dtor
|
||||
|
||||
void *obj_malloc(unsigned sz) {
|
||||
//sz = sizeof(obj) + sz + sizeof(array(obj*))); // useful?
|
||||
obj *ptr = CALLOC(1, sz);
|
||||
OBJ_CTOR_HDR(ptr,1,intern("obj"),sz,OBJTYPE_obj);
|
||||
return ptr;
|
||||
}
|
||||
int semvercmp( int v1, int v2 ) {
|
||||
return SEMVERCMP(v1, v2);
|
||||
void *obj_free(void *o) {
|
||||
if( !((obj*)o)->objrefs ) {
|
||||
obj_dtor(o);
|
||||
//obj_zero(o);
|
||||
if( ((obj*)o)->objheap ) {
|
||||
FREE(o);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return o; // cannot destroy: object is still referenced
|
||||
}
|
||||
|
||||
#if 0
|
||||
AUTORUN {
|
||||
for( int i= 0; i <= 255; ++i) printf(SEMVERFMT ",", i);
|
||||
puts("");
|
||||
// ----------------------------------------------------------------------------
|
||||
// core
|
||||
|
||||
printf(SEMVERFMT "\n", semver(3,7,7));
|
||||
printf(SEMVERFMT "\n", semver(2,7,7));
|
||||
printf(SEMVERFMT "\n", semver(1,7,7));
|
||||
printf(SEMVERFMT "\n", semver(0,7,7));
|
||||
|
||||
printf(SEMVERFMT "\n", semver(3,7,1));
|
||||
printf(SEMVERFMT "\n", semver(2,5,3));
|
||||
printf(SEMVERFMT "\n", semver(1,3,5));
|
||||
printf(SEMVERFMT "\n", semver(0,1,7));
|
||||
|
||||
assert( semvercmp( 0357, 0300 ) > 0 );
|
||||
assert( semvercmp( 0277, 0300 ) < 0 );
|
||||
assert( semvercmp( 0277, 0200 ) > 0 );
|
||||
assert( semvercmp( 0277, 0100 ) < 0 );
|
||||
assert( semvercmp( 0076, 0070 ) == 0 );
|
||||
assert( semvercmp( 0076, 0077 ) == 0 );
|
||||
assert( semvercmp( 0176, 0170 ) == 0 );
|
||||
assert( semvercmp( 0176, 0177 ) == 0 );
|
||||
assert( semvercmp( 0276, 0270 ) == 0 );
|
||||
assert( semvercmp( 0276, 0277 ) == 0 );
|
||||
assert( semvercmp( 0376, 0370 ) == 0 );
|
||||
assert( semvercmp( 0376, 0377 ) == 0 );
|
||||
uintptr_t obj_header(const void *o) {
|
||||
return ((obj*)o)->objheader;
|
||||
}
|
||||
uintptr_t obj_id(const void *o) {
|
||||
return ((obj*)o)->objid;
|
||||
}
|
||||
unsigned obj_typeid(const void *o) {
|
||||
return ((obj*)o)->objtype;
|
||||
}
|
||||
const char *obj_type(const void *o) {
|
||||
return OBJTYPES[ (((obj*)o)->objtype) ];
|
||||
}
|
||||
const char *obj_name(const void *o) {
|
||||
return quark(((obj*)o)->objnameid);
|
||||
}
|
||||
int obj_sizeof(const void *o) {
|
||||
return (int)( ((const obj*)o)->objsizew << OBJ_MIN_PRAGMAPACK_BITS );
|
||||
}
|
||||
int obj_size(const void *o) { // size of all members together in struct. may include padding bytes.
|
||||
static int obj_sizes[256] = {0};
|
||||
unsigned objtypeid = ((obj*)o)->objtype;
|
||||
if( objtypeid > 1 && !obj_sizes[objtypeid] ) { // check reflection for a more accurate objsize (without padding bits)
|
||||
reflect_init();
|
||||
array(reflect_t) *found = map_find(members, intern(obj_type(o)));
|
||||
if(!found)
|
||||
obj_sizes[objtypeid] = obj_sizeof(o) - sizeof(obj); // @fixme: -= sizeof(entity);
|
||||
else
|
||||
for each_array_ptr(*found, reflect_t, it)
|
||||
obj_sizes[objtypeid] += it->bytes;
|
||||
}
|
||||
return obj_sizes[objtypeid];
|
||||
}
|
||||
char *obj_data(void *o) { // pointer to the first member in struct
|
||||
return (char*)o + sizeof(obj);
|
||||
}
|
||||
const char *obj_datac(const void *o) { // const pointer to the first struct member
|
||||
return (const char*)o + sizeof(obj);
|
||||
}
|
||||
void* obj_payload(const void *o) { // pointer right after last member in struct
|
||||
return (char*)o + (((obj*)o)->objsizew<<OBJ_MIN_PRAGMAPACK_BITS);
|
||||
}
|
||||
void *obj_zero(void *o) { // reset all object members
|
||||
return memset(obj_data(o), 0, obj_size(o)), o;
|
||||
}
|
||||
|
||||
static
|
||||
void test_obj_core() {
|
||||
obj *r = obj_new_ext(obj, "root");
|
||||
obj *s = obj_new_ext(obj, "root");
|
||||
|
||||
test(r);
|
||||
test( 0 == strcmp(obj_type(r), "obj") );
|
||||
test( 0 == strcmp(obj_name(r), "root") );
|
||||
test( OBJTYPE_obj == obj_typeid(r) );
|
||||
|
||||
test(s);
|
||||
test( 0 == strcmp(obj_type(s), "obj") );
|
||||
test( 0 == strcmp(obj_name(s), "root") );
|
||||
test( OBJTYPE_obj == obj_typeid(s) );
|
||||
|
||||
test( obj_id(r) != 0 );
|
||||
test( obj_id(s) != 0 );
|
||||
test( obj_id(r) != obj_id(s) );
|
||||
|
||||
obj t = obj_ext(obj, "root");
|
||||
obj u = obj_ext(obj, "root");
|
||||
|
||||
test(&t);
|
||||
test( 0 == strcmp(obj_type(&t), "obj") );
|
||||
test( 0 == strcmp(obj_name(&t), "root") );
|
||||
test( OBJTYPE_obj == obj_typeid(&t) );
|
||||
|
||||
test(&u);
|
||||
test( 0 == strcmp(obj_type(&u), "obj") );
|
||||
test( 0 == strcmp(obj_name(&u), "root") );
|
||||
test( OBJTYPE_obj == obj_typeid(&u) );
|
||||
|
||||
test( obj_id(&t) == 0 );
|
||||
test( obj_id(&u) == 0 );
|
||||
test( obj_id(&t) == obj_id(&u) );
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// refcounting
|
||||
|
||||
// static int __thread global_ref_count; // @fixme: make it atomic
|
||||
// static void objref_check_atexit(void) {
|
||||
// if(global_ref_count) tty_color(YELLOW), fprintf(stderr, "Warn! obj_refs not zero (%d)\n", global_ref_count), tty_color(0);
|
||||
// }
|
||||
// AUTORUN { (atexit)(objref_check_atexit); }
|
||||
|
||||
void *obj_ref(void *oo) {
|
||||
obj* o = (obj*)oo;
|
||||
int num = o->objrefs;
|
||||
++o->objrefs;
|
||||
assert( num < o->objrefs && "Object referenced too many times");
|
||||
//++global_ref_count;
|
||||
return o;
|
||||
}
|
||||
void *obj_unref(void *oo) {
|
||||
obj* o = (obj*)oo;
|
||||
if( o->objrefs ) --o->objrefs;
|
||||
if( o->objrefs ) return o;
|
||||
obj_free(o);
|
||||
//--global_ref_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// scene tree
|
||||
|
||||
array(obj*)* obj_children(const void *o) {
|
||||
array(obj*) *c = obj_payload(o);
|
||||
if(!(*c)) array_push((*c), NULL); // default parenting: none. @todo: optimize & move this at construction time
|
||||
return c;
|
||||
}
|
||||
obj* obj_parent(const void *o) {
|
||||
array(obj*) *c = obj_children(o);
|
||||
return (*c) ? 0[*c] : NULL;
|
||||
}
|
||||
obj* obj_root(const void *o) {
|
||||
while( obj_parent(o) ) o = obj_parent(o);
|
||||
return (obj*)o;
|
||||
}
|
||||
array(obj*)* obj_siblings(const void *o) {
|
||||
return obj_children(obj_parent(o));
|
||||
}
|
||||
|
||||
static
|
||||
obj* obj_reparent(obj *o, const void *p) {
|
||||
array(obj*) *c = obj_children(o);
|
||||
0[*c] = (void*)p;
|
||||
return o;
|
||||
}
|
||||
|
||||
obj* obj_detach(void *c) {
|
||||
obj *p = obj_parent(c);
|
||||
if( p ) {
|
||||
uintptr_t id = obj_id(c);
|
||||
|
||||
array(obj*) *oo = obj_children(p);
|
||||
for( int i = 1, end = array_count(*oo); i < end; ++i) {
|
||||
obj *v = (*oo)[i];
|
||||
{
|
||||
if( obj_id(v) == id ) {
|
||||
obj_reparent(c, 0);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
obj* obj_attach(void *o, void *c) {
|
||||
// reattach
|
||||
obj_detach(c);
|
||||
obj_reparent(c, o);
|
||||
// insert into children
|
||||
array(obj*) *p = obj_children(o);
|
||||
array_push(*p, c);
|
||||
return o;
|
||||
}
|
||||
|
||||
int obj_dumptree(const void *o) {
|
||||
static int tabs = 0;
|
||||
printf("%*s" "+- %s\n", tabs++, "", obj_name(o));
|
||||
for each_objchild(o, obj, v) {
|
||||
obj_dumptree(v);
|
||||
}
|
||||
--tabs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void test_obj_scene() {
|
||||
obj *r = obj_new_ext(obj, "root"); // root
|
||||
obj *c1 = obj_new_ext(obj, "child1"); // child1
|
||||
obj *c2 = obj_new_ext(obj, "child2"); // child2
|
||||
obj *gc1 = obj_new_ext(obj, "grandchild1"); // grandchild1
|
||||
obj *gc2 = obj_new_ext(obj, "grandchild2"); // grandchild2
|
||||
obj *gc3 = obj_new_ext(obj, "grandchild3"); // grandchild3
|
||||
|
||||
test( !obj_parent(r) );
|
||||
test( !obj_parent(c1) );
|
||||
test( !obj_parent(c2) );
|
||||
test( !obj_parent(gc1) );
|
||||
test( !obj_parent(gc2) );
|
||||
test( !obj_parent(gc3) );
|
||||
test( obj_root(r) == r );
|
||||
test( obj_root(c1) == c1 );
|
||||
test( obj_root(c2) == c2 );
|
||||
test( obj_root(gc1) == gc1 );
|
||||
test( obj_root(gc2) == gc2 );
|
||||
test( obj_root(gc3) == gc3 );
|
||||
|
||||
// r
|
||||
obj_attach(r, c1); // +- c1
|
||||
obj_attach(c1, gc1); // +- gc1
|
||||
obj_attach(r, c2); // +- c2
|
||||
obj_attach(c2, gc2); // +- gc2
|
||||
obj_attach(c2, gc3); // +- gc3
|
||||
|
||||
// obj_dumptree(r);
|
||||
// puts("---");
|
||||
|
||||
test( obj_parent(r) == 0 );
|
||||
test( obj_parent(c1) == r );
|
||||
test( obj_parent(c2) == r );
|
||||
test( obj_parent(gc1) == c1 );
|
||||
test( obj_parent(gc2) == c2 );
|
||||
test( obj_parent(gc3) == c2 );
|
||||
|
||||
test( obj_root(r) == r );
|
||||
test( obj_root(c1) == r );
|
||||
test( obj_root(c2) == r );
|
||||
test( obj_root(gc1) == r );
|
||||
test( obj_root(gc2) == r );
|
||||
test( obj_root(gc3) == r );
|
||||
|
||||
for each_objchild(r, obj, o) test( o == c1 || o == c2 );
|
||||
for each_objchild(c1, obj, o) test( o == gc1 );
|
||||
for each_objchild(c2, obj, o) test( o == gc2 || o == gc3 );
|
||||
|
||||
obj_detach(c1);
|
||||
test( !obj_parent(c1) );
|
||||
for each_objchild(r, obj, o) test( o != c1 );
|
||||
for each_objchild(c1, obj, o) test( o == gc1 );
|
||||
|
||||
obj_detach(c2);
|
||||
test( !obj_parent(c2) );
|
||||
for each_objchild(r, obj, o) test( o != c2 );
|
||||
for each_objchild(c2, obj, o) test( o == gc2 || o == gc3 );
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// metadata
|
||||
|
||||
static map(int,int) oms;
|
||||
static thread_mutex_t *oms_lock;
|
||||
const char *obj_metaset(const void *o, const char *key, const char *value) {
|
||||
do_threadlock(oms_lock) {
|
||||
if(!oms) map_init_int(oms);
|
||||
int *q = map_find_or_add(oms, intern(va("%llu-%s",obj_id((obj*)o),key)), 0);
|
||||
if(!*q && !value[0]) {} else *q = intern(value);
|
||||
return quark(*q);
|
||||
}
|
||||
return 0; // unreachable
|
||||
}
|
||||
const char* obj_metaget(const void *o, const char *key) {
|
||||
do_threadlock(oms_lock) {
|
||||
if(!oms) map_init_int(oms);
|
||||
int *q = map_find_or_add(oms, intern(va("%llu-%s",obj_id((obj*)o),key)), 0);
|
||||
return quark(*q);
|
||||
}
|
||||
return 0; // unreachable
|
||||
}
|
||||
|
||||
static
|
||||
void test_obj_metadatas( void *o1 ) {
|
||||
obj *o = (obj *)o1;
|
||||
test( !strcmp("", obj_metaget(o, "has_passed_test")) );
|
||||
test( !strcmp("yes", obj_metaset(o, "has_passed_test", "yes")) );
|
||||
test( !strcmp("yes", obj_metaget(o, "has_passed_test")) );
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// stl
|
||||
|
||||
void* obj_swap(void *dst, void *src) { // @testme
|
||||
int len = obj_size(dst);
|
||||
char *buffer = ALLOCA(len);
|
||||
memcpy(buffer, obj_datac(dst), len);
|
||||
memcpy(obj_data(dst), obj_datac(src), len);
|
||||
memcpy(obj_data(src), buffer, len);
|
||||
return dst;
|
||||
}
|
||||
|
||||
void* obj_copy_fast(void *dst, const void *src) {
|
||||
// note: prefer obj_copy() as it should handle pointers and guids as well
|
||||
return memcpy(obj_data(dst), obj_datac(src), obj_size(dst));
|
||||
}
|
||||
void* obj_copy(void *dst, const void *src) { // @testme
|
||||
// @todo: use obj_copy_fast() silently if the object does not contain any pointers/guids
|
||||
return obj_loadini(dst, obj_saveini(src));
|
||||
// return obj_load(dst, obj_save(src));
|
||||
// return obj_loadbin(dst, obj_savebin(src));
|
||||
// return obj_loadini(dst, obj_saveini(src));
|
||||
// return obj_loadjson(dst, obj_savejson(src));
|
||||
// return obj_loadmpack(dst, obj_savempack(src));
|
||||
}
|
||||
|
||||
int obj_comp_fast(const void *a, const void *b) {
|
||||
// note: prefer obj_comp() as it should handle pointers and guids as well
|
||||
return memcmp(obj_datac(a), obj_datac(b), obj_size(a));
|
||||
}
|
||||
int obj_comp(const void *a, const void *b) {
|
||||
// @todo: use obj_comp_fast() silently if the object does not contain any pointers/guids
|
||||
return strcmp(obj_saveini(a),obj_saveini(b));
|
||||
}
|
||||
int obj_lesser(const void *a, const void *b) {
|
||||
return obj_comp(a,b) < 0;
|
||||
}
|
||||
int obj_greater(const void *a, const void *b) {
|
||||
return obj_comp(a,b) > 0;
|
||||
}
|
||||
int obj_equal(const void *a, const void *b) {
|
||||
return obj_comp(a,b) == 0;
|
||||
}
|
||||
|
||||
uint64_t obj_hash(const void *o) {
|
||||
return hash_bin(obj_datac(o), obj_size(o));
|
||||
}
|
||||
|
||||
static
|
||||
void test_obj_similarity(void *o1, void *o2) {
|
||||
obj *b = (obj*)o1;
|
||||
obj *c = (obj*)o2;
|
||||
test( 0 == strcmp(obj_name(b),obj_name(c)) );
|
||||
test( 0 == strcmp(obj_type(b),obj_type(c)) );
|
||||
}
|
||||
static
|
||||
void test_obj_equality(void *o1, void *o2) {
|
||||
obj *b = (obj*)o1;
|
||||
obj *c = (obj*)o2;
|
||||
test_obj_similarity(b, c);
|
||||
test( obj_size(b) == obj_size(c) );
|
||||
test( obj_hash(b) == obj_hash(c) );
|
||||
test( 0 == obj_comp(b,c) );
|
||||
test( obj_equal(b,c) );
|
||||
test( !obj_lesser(b,c) );
|
||||
test( !obj_greater(b,c) );
|
||||
}
|
||||
static
|
||||
void test_obj_exact(void *o1, void *o2) {
|
||||
obj *b = (obj*)o1;
|
||||
obj *c = (obj*)o2;
|
||||
test_obj_equality(b, c);
|
||||
test( obj_header(b) == obj_header(c) );
|
||||
test( 0 == memcmp(b, c, obj_sizeof(b)) );
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// debug
|
||||
|
||||
bool obj_hexdump(const void *oo) {
|
||||
const obj *o = (const obj *)oo;
|
||||
int header = 1 * sizeof(obj);
|
||||
printf("; name[%s] type[%s] id[%d..%d] unused[%08x] sizeof[%02d] %llx\n",
|
||||
obj_name(o), obj_type(o),
|
||||
(int)o->objid>>16, (int)o->objid&0xffff, (int)o->objunused,
|
||||
obj_sizeof(o), o->objheader);
|
||||
return hexdump(obj_datac(o) - header, obj_size(o) + header), 1;
|
||||
}
|
||||
int obj_print(const void *o) {
|
||||
char *sav = obj_saveini(o); // obj_savejson(o)
|
||||
return puts(sav);
|
||||
}
|
||||
static char *obj_tempname = 0;
|
||||
static FILE *obj_filelog = 0;
|
||||
int (obj_printf)(const void *o, const char *text) {
|
||||
if( !obj_tempname ) {
|
||||
obj_tempname = stringf("%s.log", app_name());
|
||||
unlink(obj_tempname);
|
||||
obj_filelog = fopen(obj_tempname, "w+b");
|
||||
if( obj_filelog ) fseek(obj_filelog, 0L, SEEK_SET);
|
||||
}
|
||||
int rc = 0;
|
||||
for( char *end; (end = strchr(text, '\n')) != NULL; ) {
|
||||
rc |= fprintf(obj_filelog, "[%p] %.*s\n", o, (int)(end - text), text );
|
||||
text = end + 1;
|
||||
}
|
||||
if( text[0] ) rc |= fprintf(obj_filelog, "[%p] %s\n", o, text);
|
||||
return rc;
|
||||
}
|
||||
int obj_console(const void *o) { // obj_output() ?
|
||||
if( obj_filelog ) fflush(obj_filelog);
|
||||
return obj_tempname && !system(va(ifdef(win32,"type \"%s\" | find \"[%p]\"", "cat %s | grep \"[%p]\""), obj_tempname, o));
|
||||
}
|
||||
|
||||
static
|
||||
void test_obj_console(void *o1) {
|
||||
obj *o = (obj *)o1;
|
||||
|
||||
obj_printf(o, "this is [%s], line 1\n", obj_name(o));
|
||||
obj_printf(NULL, "this line does not belong to any object\n");
|
||||
obj_printf(o, "this is [%s], line 2\n", obj_name(o));
|
||||
obj_console(o);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// serialization
|
||||
|
||||
const char *p2s(const char *type, void *p) {
|
||||
// @todo: p2s(int interned_type, void *p)
|
||||
/**/ if( !strcmp(type, "int") ) return itoa1(*(int*)p);
|
||||
else if( !strcmp(type, "unsigned") ) return itoa1(*(unsigned*)p);
|
||||
else if( !strcmp(type, "float") ) return ftoa1(*(float*)p);
|
||||
else if( !strcmp(type, "double") ) return ftoa1(*(double*)p);
|
||||
else if( !strcmp(type, "uintptr_t") ) return va("%08llx", *(uintptr_t*)p);
|
||||
else if( !strcmp(type, "vec2i") ) return itoa2(*(vec2i*)p);
|
||||
else if( !strcmp(type, "vec3i") ) return itoa3(*(vec3i*)p);
|
||||
else if( !strcmp(type, "vec2") ) return ftoa2(*(vec2*)p);
|
||||
else if( !strcmp(type, "vec3") ) return ftoa3(*(vec3*)p);
|
||||
else if( !strcmp(type, "vec4") ) return ftoa4(*(vec4*)p);
|
||||
else if( !strcmp(type, "char*") || !strcmp(type, "string") ) return va("%s", *(char**)p);
|
||||
// @todo: if strchr('*') assume obj, if reflected save guid: obj_id();
|
||||
return tty_color(YELLOW), printf("p2s: cannot serialize `%s` type\n", type), tty_color(0), "";
|
||||
}
|
||||
bool s2p(void *P, const char *type, const char *str) {
|
||||
int i; unsigned u; float f; double g; char *s = 0; uintptr_t p;
|
||||
vec2 v2; vec3 v3; vec4 v4; vec2i v2i; vec3i v3i;
|
||||
/**/ if( !strcmp(type, "int") ) return !!memcpy(P, (i = atoi1(str), &i), sizeof(i));
|
||||
else if( !strcmp(type, "unsigned") ) return !!memcpy(P, (u = atoi1(str), &u), sizeof(u));
|
||||
else if( !strcmp(type, "vec2i") ) return !!memcpy(P, (v2i = atoi2(str), &v2i), sizeof(v2i));
|
||||
else if( !strcmp(type, "vec3i") ) return !!memcpy(P, (v3i = atoi3(str), &v3i), sizeof(v3i));
|
||||
else if( !strcmp(type, "float") ) return !!memcpy(P, (f = atof1(str), &f), sizeof(f));
|
||||
else if( !strcmp(type, "double") ) return !!memcpy(P, (g = atof1(str), &g), sizeof(g));
|
||||
else if( !strcmp(type, "vec2") ) return !!memcpy(P, (v2 = atof2(str), &v2), sizeof(v2));
|
||||
else if( !strcmp(type, "vec3") ) return !!memcpy(P, (v3 = atof3(str), &v3), sizeof(v3));
|
||||
else if( !strcmp(type, "vec4") ) return !!memcpy(P, (v4 = atof4(str), &v4), sizeof(v4));
|
||||
else if( !strcmp(type, "uintptr_t") ) return !!memcpy(P, (p = strtol(str, NULL, 16), &p), sizeof(p));
|
||||
else if( !strcmp(type, "char*") || !strcmp(type, "string") ) {
|
||||
char substring[128] = {0};
|
||||
sscanf(str, "%[^\r\n]", substring);
|
||||
|
||||
strcatf(&s, "%s", substring);
|
||||
|
||||
*(uintptr_t*)(P) = (uintptr_t)s;
|
||||
return 1;
|
||||
}
|
||||
// @todo: if strchr('*') assume obj, if reflected load guid: obj_id();
|
||||
return tty_color(YELLOW), printf("s2p: cannot deserialize `%s` type\n", type), tty_color(0), 0;
|
||||
}
|
||||
|
||||
char *obj_saveini(const void *o) { // @testme
|
||||
char *out = 0;
|
||||
const char *T = obj_type(o);
|
||||
strcatf(&out, "[%s] ; v100\n", T);
|
||||
for each_member(T,R) {
|
||||
const char *sav = p2s(R->type,(char*)(o)+R->sz);
|
||||
if(!sav) return FREE(out), NULL;
|
||||
strcatf(&out,"%s.%s=%s\n", R->type,R->name,sav );
|
||||
}
|
||||
char *cpy = va("%s", out);
|
||||
FREE(out);
|
||||
return cpy;
|
||||
}
|
||||
obj *obj_mergeini(void *o, const char *ini) { // @testme
|
||||
const char *sqr = strchr(ini, '[');
|
||||
if( !sqr ) return 0;
|
||||
ini = sqr+1;
|
||||
|
||||
char T[64] = {0};
|
||||
if( sscanf(ini, "%64[^]]", &T) != 1 ) return 0; // @todo: parse version as well
|
||||
ini += strlen(T);
|
||||
|
||||
for each_member(T,R) {
|
||||
char *lookup = va("\n%s.%s=", R->type,R->name), *found = 0;
|
||||
|
||||
// type needed? /*
|
||||
if(!found) { found = strstr(ini, lookup); if (found) found += strlen(lookup); }
|
||||
if(!found) { *lookup = '\r'; }
|
||||
if(!found) { found = strstr(ini, lookup); if (found) found += strlen(lookup); }
|
||||
// */
|
||||
|
||||
if(!found) lookup = va("\n%s=", R->name);
|
||||
|
||||
if(!found) { found = strstr(ini, lookup); if (found) found += strlen(lookup); }
|
||||
if(!found) { *lookup = '\r'; }
|
||||
if(!found) { found = strstr(ini, lookup); if (found) found += strlen(lookup); }
|
||||
|
||||
if( found) {
|
||||
if(!s2p((char*)(o)+R->sz, R->type, found))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
obj *obj_loadini(void *o, const char *ini) { // @testme
|
||||
return obj_mergeini(obj_zero(o), ini);
|
||||
}
|
||||
|
||||
char *obj_savejson(const void *o) {
|
||||
char *j = 0;
|
||||
const char *T = obj_type(o);
|
||||
for each_member(T,R) {
|
||||
const char *sav = p2s(R->type,(char*)(o)+R->sz);
|
||||
if(!sav) return FREE(j), NULL;
|
||||
char is_string = !strcmp(R->type,"char*") || !strcmp(R->type,"string");
|
||||
strcatf(&j," %s: %s%s%s,\n", R->name,is_string?"\"":"",sav,is_string?"\"":"" );
|
||||
}
|
||||
char *out = va("%s: { // v100\n%s}\n", T,j);
|
||||
FREE(j);
|
||||
#if is(debug)
|
||||
json5 root = { 0 };
|
||||
char *error = json5_parse(&root, va("%s", out), 0);
|
||||
assert( !error );
|
||||
json5_free(&root);
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
obj *obj_mergejson(void *o, const char *json) {
|
||||
// @fixme: va() call below could be optimized out since we could figure it out if json was internally provided (via va or strdup), or user-provided
|
||||
json5 root = { 0 };
|
||||
char *error = json5_parse(&root, va("%s", json), 0); // @todo: parse version comment
|
||||
if( !error && root.type == JSON5_OBJECT && root.count == 1 ) {
|
||||
json5 *n = &root.nodes[0];
|
||||
char *T = n->name;
|
||||
for each_member(T,R) {
|
||||
for( int i = 0; i < n->count; ++i ) {
|
||||
if( !strcmp(R->name, n->nodes[i].name) ) {
|
||||
void *p = (char*)o + R->sz;
|
||||
/**/ if( n->nodes[i].type == JSON5_UNDEFINED ) {}
|
||||
else if( n->nodes[i].type == JSON5_NULL ) {
|
||||
*(uintptr_t*)(p) = (uintptr_t)0;
|
||||
}
|
||||
else if( n->nodes[i].type == JSON5_BOOL ) {
|
||||
*(bool*)p = n->nodes[i].boolean;
|
||||
}
|
||||
else if( n->nodes[i].type == JSON5_INTEGER ) {
|
||||
if( strstr(R->type, "64" ) )
|
||||
*(int64_t*)p = n->nodes[i].integer;
|
||||
else
|
||||
*(int*)p = n->nodes[i].integer;
|
||||
}
|
||||
else if( n->nodes[i].type == JSON5_STRING ) {
|
||||
char *s = 0;
|
||||
strcatf(&s, "%s", n->nodes[i].string);
|
||||
*(uintptr_t*)(p) = (uintptr_t)s;
|
||||
}
|
||||
else if( n->nodes[i].type == JSON5_REAL ) {
|
||||
if( R->type[0] == 'f' )
|
||||
*(float*)(p) = n->nodes[i].real;
|
||||
else
|
||||
*(double*)(p) = n->nodes[i].real;
|
||||
}
|
||||
else if( n->nodes[i].type == JSON5_OBJECT ) {}
|
||||
else if( n->nodes[i].type == JSON5_ARRAY ) {}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
json5_free(&root);
|
||||
return error ? 0 : o;
|
||||
}
|
||||
obj *obj_loadjson(void *o, const char *json) { // @testme
|
||||
return obj_mergejson(obj_zero(o), json);
|
||||
}
|
||||
|
||||
char *obj_savebin(const void *o) { // PACKMSG("ss", "entity_v1", quark(self->objnameid)); // = PACKMSG("p", obj_data(&b), (uint64_t)obj_size(&b));
|
||||
int len = cobs_bounds(obj_size(o));
|
||||
char *sav = va("%*.s", len, "");
|
||||
len = cobs_encode(obj_datac(o), obj_size(o), sav, len);
|
||||
sav[len] = '\0';
|
||||
return sav;
|
||||
}
|
||||
obj *obj_mergebin(void *o, const char *sav) { // UNPACKMSG(sav, "p", obj_data(c), (uint64_t)obj_size(c));
|
||||
int outlen = cobs_decode(sav, strlen(sav), obj_data(o), obj_size(o));
|
||||
return outlen != obj_size(o) ? NULL : o;
|
||||
}
|
||||
obj *obj_loadbin(void *o, const char *sav) {
|
||||
return obj_mergebin(obj_zero(o), sav);
|
||||
}
|
||||
|
||||
char *obj_savempack(const void *o) { // @todo
|
||||
return "";
|
||||
}
|
||||
obj *obj_mergempack(void *o, const char *sav) { // @todo
|
||||
return 0;
|
||||
}
|
||||
obj *obj_loadmpack(void *o, const char *sav) { // @todo
|
||||
return obj_mergempack(obj_zero(o), sav);
|
||||
}
|
||||
|
||||
static __thread array(char*) obj_stack;
|
||||
int obj_push(const void *o) {
|
||||
char *bin = STRDUP(obj_savebin(o));
|
||||
array_push(obj_stack, bin);
|
||||
return 1;
|
||||
}
|
||||
int obj_pop(void *o) {
|
||||
char *bin = *array_pop(obj_stack);
|
||||
int rc = !!obj_loadbin(o, bin);
|
||||
return FREE(bin), rc;
|
||||
}
|
||||
|
||||
static
|
||||
void test_obj_serialization(void *o1, void *o2) {
|
||||
obj* b = (obj*)o1;
|
||||
obj* c = (obj*)o2;
|
||||
|
||||
char *json = obj_savejson(b); // puts(json);
|
||||
test( json[0] );
|
||||
char *ini = obj_saveini(b); // puts(ini);
|
||||
test( ini[0] );
|
||||
char *bin = obj_savebin(b); // puts(bin);
|
||||
test( bin[0] );
|
||||
|
||||
obj_push(c);
|
||||
|
||||
test( obj_copy(c,b) );
|
||||
test( obj_comp(b,c) == 0 ) || obj_hexdump(b) & obj_hexdump(c);
|
||||
|
||||
test( obj_zero(c) );
|
||||
test( obj_comp(c,b) != 0 ) || obj_hexdump(c);
|
||||
test( obj_loadbin(c, bin) );
|
||||
test( obj_comp(c,b) == 0 ) || obj_hexdump(c) & obj_hexdump(b);
|
||||
|
||||
test( obj_zero(c) );
|
||||
test( obj_comp(c,b) != 0 ) || obj_hexdump(c);
|
||||
test( obj_loadini(c, ini) );
|
||||
test( obj_comp(c,b) == 0 ) || obj_hexdump(c) & obj_hexdump(b);
|
||||
|
||||
test( obj_zero(c) );
|
||||
test( obj_comp(c,b) != 0 ) || obj_hexdump(c);
|
||||
test( obj_loadjson(c, json) );
|
||||
test( obj_comp(c,b) == 0 ) || obj_hexdump(c) & obj_hexdump(b);
|
||||
|
||||
obj_pop(c);
|
||||
obj_hexdump(c);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// components
|
||||
|
||||
bool obj_addcomponent(void *object, unsigned c, void *ptr) {
|
||||
entity *e = (entity*)object;
|
||||
e->cflags |= (3ULL << c);
|
||||
e->c[c & (OBJCOMPONENTS_MAX-1)] = ptr;
|
||||
return 1;
|
||||
}
|
||||
bool obj_hascomponent(void *object, unsigned c) {
|
||||
entity *e = (entity*)object;
|
||||
return !!(e->cflags & (3ULL << c));
|
||||
}
|
||||
void* obj_getcomponent(void *object, unsigned c) {
|
||||
entity *e = (entity*)object;
|
||||
return e->c[c & (OBJCOMPONENTS_MAX-1)];
|
||||
}
|
||||
bool obj_delcomponent(void *object, unsigned c) {
|
||||
entity *e = (entity*)object;
|
||||
e->cflags &= ~(3ULL << c);
|
||||
e->c[c & (OBJCOMPONENTS_MAX-1)] = NULL;
|
||||
return 1;
|
||||
}
|
||||
bool obj_usecomponent(void *object, unsigned c) {
|
||||
entity *e = (entity*)object;
|
||||
e->cflags |= (1ULL << c);
|
||||
return 1;
|
||||
}
|
||||
bool obj_offcomponent(void *object, unsigned c) {
|
||||
entity *e = (entity*)object;
|
||||
e->cflags &= ~(1ULL << c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *entity_save(entity *self) {
|
||||
char *sav = obj_saveini(self);
|
||||
return sav;
|
||||
}
|
||||
|
||||
AUTORUN {
|
||||
STRUCT(entity, uintptr_t, cflags);
|
||||
|
||||
// struct { OBJHEADER union { struct { uintptr_t objenabled : 32, objflagged : 32; }; uintptr_t cflags; }; void* c[32]; };
|
||||
|
||||
obj_extend(entity, save);
|
||||
}
|
||||
|
||||
static
|
||||
void test_obj_ecs() {
|
||||
entity *e = obj_new(entity);
|
||||
puts(obj_save(e));
|
||||
|
||||
for( int i = 0; i < 32; ++i) test(0 == obj_hascomponent(e, i));
|
||||
for( int i = 0; i < 32; ++i) test(1 == obj_addcomponent(e, i, NULL));
|
||||
for( int i = 0; i < 32; ++i) test(1 == obj_hascomponent(e, i));
|
||||
for( int i = 0; i < 32; ++i) test(1 == obj_delcomponent(e, i));
|
||||
for( int i = 0; i < 32; ++i) test(0 == obj_hascomponent(e, i));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// reflection
|
||||
|
||||
void *obj_clone(const void *src) {
|
||||
obj *ptr = obj_malloc( sizeof(obj) + obj_size(src) + sizeof(array(obj*)) );
|
||||
ptr->objheader = ((const obj *)src)->objheader;
|
||||
obj_loadini(ptr, obj_saveini(src));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* obj_merge(void *dst, const void *src) { // @testme
|
||||
char *bin = obj_savebin(src);
|
||||
return obj_mergebin(dst, bin);
|
||||
}
|
||||
|
||||
void* obj_mutate(void **dst, const void *src) {
|
||||
#if 0
|
||||
// mutate a class. ie, convert a given object class into a different one,
|
||||
// while preserving the original metas, components and references as much as possible.
|
||||
// @todo iterate per field
|
||||
|
||||
if(!*dst_) return *dst_ = obj_clone(src);
|
||||
|
||||
void *dst = *dst_;
|
||||
dtor(dst);
|
||||
|
||||
unsigned src_sz = obj_sizeof(src);
|
||||
unsigned src_id = obj_id(src);
|
||||
|
||||
void *dst_ptr = *((void**)dst - 1);
|
||||
unsigned payload = (OBJPAYLOAD16(dst_ptr) & 255) | src_id << 8;
|
||||
FREE( OBJUNBOX(dst_ptr) );
|
||||
*((void**)dst - 1) = OBJBOX( STRDUP( OBJUNBOX(*((void**)src - 1)) ), payload);
|
||||
|
||||
void *base = (void*)((void**)dst - 1);
|
||||
base = REALLOC(base, src_sz + sizeof(void*));
|
||||
*dst_ = (char*)base + sizeof(void*);
|
||||
dst = (char*)base + sizeof(void*);
|
||||
memcpy(dst, src, src_sz);
|
||||
|
||||
ctor(dst);
|
||||
#endif
|
||||
return dst;
|
||||
}
|
||||
|
||||
void *obj_make(const char *str) {
|
||||
const char *T;
|
||||
const char *I = strchr(str, '['); // is_ini
|
||||
const char *J = strchr(str, '{'); // is_json
|
||||
if( !I && !J ) return 0;
|
||||
else if( I && !J ) T = I;
|
||||
else if( !I && J ) T = J;
|
||||
else T = I < J ? I : J;
|
||||
|
||||
char name[64] = {0};
|
||||
if( sscanf(T+1, T == I ? "%64[^]]" : "%64[^:=]", &name) != 1 ) return 0;
|
||||
|
||||
int has_components = 0; // @todo: support entities too
|
||||
|
||||
unsigned Tid = intern(name);
|
||||
reflect_init();
|
||||
reflect_t *found = map_find(reflects, Tid);
|
||||
if(!found) return 0;
|
||||
|
||||
obj *ptr = CALLOC(1, found->sz + has_components * sizeof(array(obj*)));
|
||||
void *ret = (T == I ? obj_mergeini : obj_mergejson)(ptr, str);
|
||||
OBJTYPES[ found->objtype ] = found->name;
|
||||
OBJ_CTOR_PTR(ptr,1,found->id,found->sz,found->objtype);
|
||||
|
||||
return ptr; // returns partial construction as well. @todo: just return `ret` for a more strict built/failed policy
|
||||
}
|
||||
|
|
|
@ -1,57 +1,306 @@
|
|||
// -----------------------------------------------------------------------------
|
||||
// semantic versioning in a single byte (octal)
|
||||
// C objects framework
|
||||
// - rlyeh, public domain.
|
||||
//
|
||||
// - single octal byte that represents semantic versioning (major.minor.patch).
|
||||
// - allowed range [0000..0377] ( <-> [0..255] decimal )
|
||||
// - comparison checks only major.minor tuple as per convention.
|
||||
// ## object limitations
|
||||
// - 8-byte overhead per object
|
||||
// - XX-byte overhead per object-entity
|
||||
// - 32 components max per object-entity
|
||||
// - 256 classes max per game
|
||||
// - 256 references max per object
|
||||
// - 1024K bytes max per object
|
||||
// - 8 generations + 64K IDs per running instance (19-bit IDs)
|
||||
// - support for pragma pack(1) structs not enabled by default.
|
||||
|
||||
API int semver( int major, int minor, int patch );
|
||||
API int semvercmp( int v1, int v2 );
|
||||
/* /!\ if you plan to use pragma pack(1) on any struct, you need #define OBJ_MIN_PRAGMAPACK_BITS 0 at the expense of max class size /!\ */
|
||||
#ifndef OBJ_MIN_PRAGMAPACK_BITS
|
||||
//#define OBJ_MIN_PRAGMAPACK_BITS 3 // allows pragma packs >= 8. objsizew becomes 7<<3, so 1024 bytes max per class (default)
|
||||
#define OBJ_MIN_PRAGMAPACK_BITS 1 // allows pragma packs >= 2. objsizew becomes 7<<1, so 256 bytes max per class
|
||||
//#define OBJ_MIN_PRAGMAPACK_BITS 0 // allows pragma packs >= 1. objsizew becomes 7<<0, so 128 bytes max per class
|
||||
#endif
|
||||
|
||||
#define SEMVER(major,minor,patch) (0100 * (major) + 010 * (minor) + (patch))
|
||||
#define SEMVERCMP(v1,v2) (((v1) & 0110) - ((v2) & 0110))
|
||||
#define SEMVERFMT "%03o"
|
||||
#define OBJHEADER \
|
||||
union { \
|
||||
uintptr_t objheader; \
|
||||
struct { \
|
||||
uintptr_t objtype:8; \
|
||||
uintptr_t objheap:1; \
|
||||
uintptr_t objsizew:7; \
|
||||
uintptr_t objrefs:8; \
|
||||
uintptr_t objcomps:1; /* << can be removed? check payload ptr instead? */ \
|
||||
uintptr_t objnameid:16; \
|
||||
uintptr_t objid:ID_INDEX_BITS+ID_COUNT_BITS; /*16+3*/ \
|
||||
uintptr_t objunused:64-8-7-1-1-8-16-ID_INDEX_BITS-ID_COUNT_BITS; /*4*/ \
|
||||
}; \
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// storage types. refer to vec2i/3i, vec2/3/4 if you plan to do math operations
|
||||
#define OBJ \
|
||||
struct { OBJHEADER };
|
||||
|
||||
typedef struct byte2 { uint8_t x,y; } byte2;
|
||||
typedef struct byte3 { uint8_t x,y,z; } byte3;
|
||||
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
|
||||
// ----------------------------------------------------------------------------
|
||||
// syntax sugars
|
||||
|
||||
typedef struct int2 { int x,y; } int2;
|
||||
typedef struct int3 { int x,y,z; } int3;
|
||||
typedef struct int4 { int x,y,z,w; } int4;
|
||||
#ifdef OBJTYPE
|
||||
#undef OBJTYPE
|
||||
#endif
|
||||
|
||||
typedef struct uint2 { unsigned int x,y; } uint2;
|
||||
typedef struct uint3 { unsigned int x,y,z; } uint3;
|
||||
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
|
||||
#define OBJTYPE(T) \
|
||||
OBJTYPE_##T
|
||||
|
||||
typedef struct float2 { float x,y; } float2;
|
||||
typedef struct float3 { float x,y,z; } float3;
|
||||
typedef struct float4 { float x,y,z,w; } float4;
|
||||
#define OBJTYPEDEF(NAME,N) \
|
||||
enum { OBJTYPE(NAME) = N }; \
|
||||
STATIC_ASSERT( N <= 255 ); \
|
||||
STATIC_ASSERT( (sizeof(NAME) & ((1<<OBJ_MIN_PRAGMAPACK_BITS)-1)) == 0 );
|
||||
|
||||
typedef struct double2 { double x,y; } double2;
|
||||
typedef struct double3 { double x,y,z; } double3;
|
||||
typedef struct double4 { double x,y,z,w; } double4;
|
||||
// ----------------------------------------------------------------------------
|
||||
// objects
|
||||
|
||||
#define byte2(x,y) M_CAST(byte2, (uint8_t)(x), (uint8_t)(y) )
|
||||
#define byte3(x,y,z) M_CAST(byte3, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z) )
|
||||
#define byte4(x,y,z,w) M_CAST(byte4, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z), (uint8_t)(w) )
|
||||
#define TYPEDEF_STRUCT(NAME,N,...) \
|
||||
typedef struct NAME { OBJ \
|
||||
__VA_ARGS__ \
|
||||
char payload[0]; \
|
||||
} NAME; OBJTYPEDEF(NAME,N);
|
||||
|
||||
#define int2(x,y) M_CAST(int2, (int)(x), (int)(y) )
|
||||
#define int3(x,y,z) M_CAST(int3, (int)(x), (int)(y), (int)(z) )
|
||||
#define int4(x,y,z,w) M_CAST(int4, (int)(x), (int)(y), (int)(z), (int)(w) )
|
||||
// TYPEDEF_STRUCT(obj,0);
|
||||
typedef struct obj { OBJ } obj;
|
||||
|
||||
#define uint2(x,y) M_CAST(uint2, (unsigned)(x), (unsigned)(y) )
|
||||
#define uint3(x,y,z) M_CAST(uint3, (unsigned)(x), (unsigned)(y), (unsigned)(z) )
|
||||
#define uint4(x,y,z,w) M_CAST(uint4, (unsigned)(x), (unsigned)(y), (unsigned)(z), (unsigned)(w) )
|
||||
// ----------------------------------------------------------------------------
|
||||
// entities
|
||||
|
||||
#define float2(x,y) M_CAST(float2, (float)(x), (float)(y) )
|
||||
#define float3(x,y,z) M_CAST(float3, (float)(x), (float)(y), (float)(z) )
|
||||
#define float4(x,y,z,w) M_CAST(float4, (float)(x), (float)(y), (float)(z), (float)(w) )
|
||||
#define OBJCOMPONENTS_MAX 32
|
||||
#define OBJCOMPONENTS_ALL_ENABLED 0xAAAAAAAAAAAAAAAAULL
|
||||
#define OBJCOMPONENTS_ALL_FLAGGED 0x5555555555555555ULL
|
||||
#define COMPONENTS_ONLY(x) ((x) & ~OBJCOMPONENTS_ALL_FLAGGED)
|
||||
|
||||
#define ENTITY \
|
||||
struct { OBJHEADER union { struct { uintptr_t objenabled:OBJCOMPONENTS_MAX, objflagged:OBJCOMPONENTS_MAX; }; uintptr_t cflags; }; void *c[OBJCOMPONENTS_MAX]; };
|
||||
|
||||
#define TYPEDEF_ENTITY(NAME,N,...) \
|
||||
typedef struct NAME { ENTITY \
|
||||
__VA_ARGS__ \
|
||||
char payload[0]; \
|
||||
} NAME; OBJTYPEDEF(NAME,N);
|
||||
|
||||
// OBJTYPEDEF(entity,1)
|
||||
typedef struct entity { ENTITY } entity;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// heap/stack ctor/dtor
|
||||
|
||||
static __thread obj *objtmp;
|
||||
#define OBJ_CTOR_HDR(PTR,HEAP,OBJ_NAMEID,SIZEOF_OBJ,OBJ_TYPE) ( \
|
||||
(PTR)->objheader = HEAP ? id_make(PTR) : 0, /*should assign to .objid instead. however, id_make() returns shifted bits already*/ \
|
||||
(PTR)->objnameid = (OBJ_NAMEID), \
|
||||
(PTR)->objtype = (OBJ_TYPE), \
|
||||
(PTR)->objheap = (HEAP), \
|
||||
(PTR)->objsizew = (SIZEOF_OBJ>>OBJ_MIN_PRAGMAPACK_BITS))
|
||||
#define OBJ_CTOR_PTR(PTR,HEAP,OBJ_NAMEID,SIZEOF_OBJ,OBJ_TYPE) ( \
|
||||
OBJ_CTOR_HDR(PTR,HEAP,OBJ_NAMEID,SIZEOF_OBJ,OBJ_TYPE), \
|
||||
obj_ctor(PTR))
|
||||
#define OBJ_CTOR(TYPE, NAME, HEAP, PAYLOAD_SIZE, ...) (TYPE*)( \
|
||||
objtmp = (HEAP ? MALLOC(sizeof(TYPE)+(PAYLOAD_SIZE)) : ALLOCA(sizeof(TYPE)+(PAYLOAD_SIZE))), \
|
||||
*(TYPE*)objtmp = ((TYPE){ {0}, __VA_ARGS__}), \
|
||||
((PAYLOAD_SIZE) ? memset((char*)objtmp + sizeof(TYPE), 0, (PAYLOAD_SIZE)) : objtmp), \
|
||||
( OBJTYPES[ OBJTYPE(TYPE) ] = #TYPE ), \
|
||||
OBJ_CTOR_PTR(objtmp, HEAP,intern(NAME),sizeof(TYPE),OBJTYPE(TYPE)), \
|
||||
ifdef(debug, (obj_printf)(objtmp, va("%s", callstack(+16))), 0), \
|
||||
objtmp)
|
||||
|
||||
#define obj(TYPE, ...) obj_ext(TYPE, #TYPE, __VA_ARGS__)
|
||||
#define obj_ext(TYPE, NAME, ...) *OBJ_CTOR(TYPE, NAME, 0, sizeof(array(obj*)), __VA_ARGS__)
|
||||
|
||||
#define obj_new(TYPE, ...) obj_new_ext(TYPE, #TYPE, __VA_ARGS__)
|
||||
#define obj_new_ext(TYPE, NAME, ...) OBJ_CTOR(TYPE, NAME, 1, sizeof(array(obj*)), __VA_ARGS__)
|
||||
|
||||
void* obj_malloc(unsigned sz);
|
||||
void* obj_free(void *o);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// obj generics. can be extended.
|
||||
|
||||
#define obj_ctor(o,...) obj_method(ctor, o, ##__VA_ARGS__)
|
||||
#define obj_dtor(o,...) obj_method(dtor, o, ##__VA_ARGS__)
|
||||
|
||||
#define obj_save(o,...) obj_method(save, o, ##__VA_ARGS__)
|
||||
#define obj_load(o,...) obj_method(load, o, ##__VA_ARGS__)
|
||||
|
||||
#define obj_test(o,...) obj_method(test, o, ##__VA_ARGS__)
|
||||
|
||||
#define obj_init(o,...) obj_method(init, o, ##__VA_ARGS__)
|
||||
#define obj_quit(o,...) obj_method(quit, o, ##__VA_ARGS__)
|
||||
#define obj_tick(o,...) obj_method(tick, o, ##__VA_ARGS__)
|
||||
#define obj_draw(o,...) obj_method(draw, o, ##__VA_ARGS__)
|
||||
|
||||
#define obj_lerp(o,...) obj_method(lerp, o, ##__VA_ARGS__)
|
||||
#define obj_edit(o,...) obj_method(edit, o, ##__VA_ARGS__)
|
||||
|
||||
// --- syntax sugars
|
||||
|
||||
#define obj_extend(T,func) (obj_##func[OBJTYPE(T)] = (void*)T##_##func)
|
||||
#define obj_method(method,o,...) (obj_##method[((obj*)(o))->objtype](o,##__VA_ARGS__)) // (obj_##method[((obj*)(o))->objtype]((o), ##__VA_ARGS__))
|
||||
|
||||
#define obj_vtable(func,RC,...) RC macro(obj_##func)(){ __VA_ARGS__ }; RC (*obj_##func[256])() = { REPEAT256(macro(obj_##func)) };
|
||||
#define obj_vtable_null(func,RC) RC (*obj_##func[256])() = { 0 }; // null virtual table. will crash unless obj_extend'ed
|
||||
|
||||
#define REPEAT16(f) f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f
|
||||
#define REPEAT64(f) REPEAT16(f),REPEAT16(f),REPEAT16(f),REPEAT16(f)
|
||||
#define REPEAT256(f) REPEAT64(f),REPEAT64(f),REPEAT64(f),REPEAT64(f)
|
||||
|
||||
// --- declare vtables
|
||||
|
||||
API extern void (*obj_ctor[256])(); ///-
|
||||
API extern void (*obj_dtor[256])(); ///-
|
||||
|
||||
API extern char* (*obj_save[256])(); ///-
|
||||
API extern bool (*obj_load[256])(); ///-
|
||||
API extern int (*obj_test[256])(); ///-
|
||||
|
||||
API extern int (*obj_init[256])(); ///-
|
||||
API extern int (*obj_quit[256])(); ///-
|
||||
API extern int (*obj_tick[256])(); ///-
|
||||
API extern int (*obj_draw[256])(); ///-
|
||||
|
||||
API extern int (*obj_lerp[256])(); ///-
|
||||
API extern int (*obj_edit[256])(); ///-
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// core
|
||||
|
||||
API uintptr_t obj_header(const void *o);
|
||||
|
||||
API uintptr_t obj_id(const void *o);
|
||||
API const char* obj_name(const void *o);
|
||||
|
||||
API unsigned obj_typeid(const void *o);
|
||||
API const char* obj_type(const void *o);
|
||||
|
||||
API int obj_sizeof(const void *o);
|
||||
API int obj_size(const void *o); // size of all members together in struct. may include padding bytes.
|
||||
|
||||
API char* obj_data(void *o); // pointer to the first member in struct
|
||||
API const char* obj_datac(const void *o); // const pointer to the first struct member
|
||||
|
||||
API void* obj_payload(const void *o); // pointer right after last member in struct
|
||||
API void* obj_zero(void *o); // reset all object members
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// refcounting
|
||||
|
||||
API void* obj_ref(void *oo);
|
||||
API void* obj_unref(void *oo);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// scene tree
|
||||
|
||||
// non-recursive
|
||||
#define each_objchild(p,t,o) \
|
||||
(array(obj*)* children = obj_children(p); children; children = 0) \
|
||||
for(int _i = 1, _end = array_count(*children); _i < _end; ++_i) \
|
||||
for(t *o = (t *)((*children)[_i]); o && 0[*children]; o = 0)
|
||||
|
||||
API obj* obj_detach(void *c);
|
||||
API obj* obj_attach(void *o, void *c);
|
||||
|
||||
API obj* obj_root(const void *o);
|
||||
API obj* obj_parent(const void *o);
|
||||
API array(obj*)*obj_children(const void *o);
|
||||
API array(obj*)*obj_siblings(const void *o);
|
||||
|
||||
API int obj_dumptree(const void *o);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// metadata
|
||||
|
||||
API const char* obj_metaset(const void *o, const char *key, const char *value);
|
||||
API const char* obj_metaget(const void *o, const char *key);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// stl
|
||||
|
||||
API void* obj_swap(void *dst, void *src);
|
||||
API void* obj_copy_fast(void *dst, const void *src);
|
||||
API void* obj_copy(void *dst, const void *src);
|
||||
|
||||
API int obj_comp_fast(const void *a, const void *b);
|
||||
API int obj_comp(const void *a, const void *b);
|
||||
API int obj_lesser(const void *a, const void *b);
|
||||
API int obj_greater(const void *a, const void *b);
|
||||
API int obj_equal(const void *a, const void *b);
|
||||
|
||||
API uint64_t obj_hash(const void *o);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// debug
|
||||
|
||||
API bool obj_hexdump(const void *oo);
|
||||
API int obj_print(const void *o);
|
||||
|
||||
API int obj_printf(const void *o, const char *text);
|
||||
API int obj_console(const void *o); // obj_output() ?
|
||||
|
||||
#define obj_printf(o, ...) obj_printf(o, va(__VA_ARGS__))
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// serialization
|
||||
|
||||
API char* obj_saveini(const void *o);
|
||||
API obj* obj_mergeini(void *o, const char *ini);
|
||||
API obj* obj_loadini(void *o, const char *ini);
|
||||
|
||||
API char* obj_savejson(const void *o);
|
||||
API obj* obj_mergejson(void *o, const char *json);
|
||||
API obj* obj_loadjson(void *o, const char *json);
|
||||
|
||||
API char* obj_savebin(const void *o);
|
||||
API obj* obj_mergebin(void *o, const char *sav);
|
||||
API obj* obj_loadbin(void *o, const char *sav);
|
||||
|
||||
API char* obj_savempack(const void *o); // @todo
|
||||
API obj* obj_mergempack(void *o, const char *sav); // @todo
|
||||
API obj* obj_loadmpack(void *o, const char *sav); // @todo
|
||||
|
||||
API int obj_push(const void *o);
|
||||
API int obj_pop(void *o);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// components
|
||||
|
||||
API bool obj_addcomponent(void *object, unsigned c, void *ptr);
|
||||
API bool obj_hascomponent(void *object, unsigned c);
|
||||
API void* obj_getcomponent(void *object, unsigned c);
|
||||
API bool obj_delcomponent(void *object, unsigned c);
|
||||
API bool obj_usecomponent(void *object, unsigned c);
|
||||
API bool obj_offcomponent(void *object, unsigned c);
|
||||
|
||||
API char* entity_save(entity *self);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// reflection
|
||||
|
||||
#define each_objmember(oo,TYPE,NAME,PTR) \
|
||||
(array(reflect_t) *found_ = members_find(quark(((obj*)oo)->objnameid)); found_; found_ = 0) \
|
||||
for(int it_ = 0, end_ = array_count(*found_); it_ != end_; ++it_ ) \
|
||||
for(reflect_t *R = &(*found_)[it_]; R; R = 0 ) \
|
||||
for(const char *NAME = R->name, *TYPE = R->type; NAME || TYPE; ) \
|
||||
for(void *PTR = ((char*)oo) + R->sz ; NAME || TYPE ; NAME = TYPE = 0 )
|
||||
|
||||
API void* obj_clone(const void *src);
|
||||
API void* obj_merge(void *dst, const void *src); // @testme
|
||||
API void* obj_mutate(void **dst, const void *src);
|
||||
API void* obj_make(const char *str);
|
||||
|
||||
// built-ins
|
||||
|
||||
typedef enum OBJTYPE_BUILTINS {
|
||||
OBJTYPE_obj = 0,
|
||||
OBJTYPE_entity = 1,
|
||||
OBJTYPE_vec2 = 2,
|
||||
OBJTYPE_vec3 = 3,
|
||||
OBJTYPE_vec4 = 4,
|
||||
OBJTYPE_quat = 5,
|
||||
OBJTYPE_mat33 = 6,
|
||||
OBJTYPE_mat34 = 7,
|
||||
OBJTYPE_mat44 = 8,
|
||||
OBJTYPE_vec2i = 9,
|
||||
OBJTYPE_vec3i = 10,
|
||||
} OBJTYPE_BUILTINS;
|
||||
|
||||
#define double2(x,y) M_CAST(double2, (double)(x), (double)(y) )
|
||||
#define double3(x,y,z) M_CAST(double3, (double)(x), (double)(y), (double)(z) )
|
||||
#define double4(x,y,z,w) M_CAST(double4, (double)(x), (double)(y), (double)(z), (double)(w) )
|
||||
|
|
|
@ -1,3 +1,48 @@
|
|||
// -----------------------------------------------------------------------------
|
||||
// semantic versioning in a single byte (octal)
|
||||
// - rlyeh, public domain.
|
||||
//
|
||||
// - single octal byte that represents semantic versioning (major.minor.patch).
|
||||
// - allowed range [0000..0377] ( <-> [0..255] decimal )
|
||||
// - comparison checks only major.minor tuple as per convention.
|
||||
|
||||
int semver( int major, int minor, int patch ) {
|
||||
return SEMVER(major, minor, patch);
|
||||
}
|
||||
int semvercmp( int v1, int v2 ) {
|
||||
return SEMVERCMP(v1, v2);
|
||||
}
|
||||
|
||||
#if 0
|
||||
AUTORUN {
|
||||
for( int i= 0; i <= 255; ++i) printf(SEMVERFMT ",", i);
|
||||
puts("");
|
||||
|
||||
printf(SEMVERFMT "\n", semver(3,7,7));
|
||||
printf(SEMVERFMT "\n", semver(2,7,7));
|
||||
printf(SEMVERFMT "\n", semver(1,7,7));
|
||||
printf(SEMVERFMT "\n", semver(0,7,7));
|
||||
|
||||
printf(SEMVERFMT "\n", semver(3,7,1));
|
||||
printf(SEMVERFMT "\n", semver(2,5,3));
|
||||
printf(SEMVERFMT "\n", semver(1,3,5));
|
||||
printf(SEMVERFMT "\n", semver(0,1,7));
|
||||
|
||||
assert( semvercmp( 0357, 0300 ) > 0 );
|
||||
assert( semvercmp( 0277, 0300 ) < 0 );
|
||||
assert( semvercmp( 0277, 0200 ) > 0 );
|
||||
assert( semvercmp( 0277, 0100 ) < 0 );
|
||||
assert( semvercmp( 0076, 0070 ) == 0 );
|
||||
assert( semvercmp( 0076, 0077 ) == 0 );
|
||||
assert( semvercmp( 0176, 0170 ) == 0 );
|
||||
assert( semvercmp( 0176, 0177 ) == 0 );
|
||||
assert( semvercmp( 0276, 0270 ) == 0 );
|
||||
assert( semvercmp( 0276, 0277 ) == 0 );
|
||||
assert( semvercmp( 0376, 0370 ) == 0 );
|
||||
assert( semvercmp( 0376, 0377 ) == 0 );
|
||||
}
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// compile-time fourcc, eightcc
|
||||
|
||||
|
@ -25,24 +70,18 @@ char *cc8str(uint64_t x) {
|
|||
// ----------------------------------------------------------------------------
|
||||
// float conversion (text)
|
||||
|
||||
vec2 atof2(const char *s) {
|
||||
vec2 v = {0};
|
||||
sscanf(s, "%f,%f", &v.x, &v.y);
|
||||
return v;
|
||||
char* itoa1(int v) {
|
||||
return va("%d", v);
|
||||
}
|
||||
vec3 atof3(const char *s) {
|
||||
vec3 v = {0};
|
||||
sscanf(s, "%f,%f,%f", &v.x, &v.y, &v.z);
|
||||
return v;
|
||||
char* itoa2(vec2i v) {
|
||||
return va("%d,%d", v.x,v.y);
|
||||
}
|
||||
vec4 atof4(const char *s) {
|
||||
vec4 v = {0};
|
||||
sscanf(s, "%f,%f,%f,%f", &v.x, &v.y, &v.z, &v.w);
|
||||
return v;
|
||||
char* itoa3(vec3i v) {
|
||||
return va("%d,%d,%d", v.x,v.y,v.z);
|
||||
}
|
||||
|
||||
char* ftoa(float f) {
|
||||
return va("%f", f);
|
||||
char* ftoa1(float v) {
|
||||
return va("%f", v);
|
||||
}
|
||||
char* ftoa2(vec2 v) {
|
||||
return va("%f,%f", v.x, v.y);
|
||||
|
@ -54,6 +93,51 @@ char* ftoa4(vec4 v) {
|
|||
return va("%f,%f,%f,%f", v.x, v.y, v.z, v.w);
|
||||
}
|
||||
|
||||
float atof1(const char *s) {
|
||||
char buf[64];
|
||||
return sscanf(s, "%64[^]\r\n,}]", buf) == 1 ? (float)eval(buf) : (float)NAN;
|
||||
}
|
||||
vec2 atof2(const char *s) {
|
||||
vec2 v = { 0 };
|
||||
char buf1[64],buf2[64];
|
||||
int num = sscanf(s, "%64[^]\r\n,}],%64[^]\r\n,}]", buf1, buf2);
|
||||
if( num > 0 ) v.x = eval(buf1);
|
||||
if( num > 1 ) v.y = eval(buf2);
|
||||
return v;
|
||||
}
|
||||
vec3 atof3(const char *s) {
|
||||
vec3 v = {0};
|
||||
char buf1[64],buf2[64],buf3[64];
|
||||
int num = sscanf(s, "%64[^]\r\n,}],%64[^]\r\n,}],%64[^]\r\n,}]", buf1, buf2, buf3);
|
||||
if( num > 0 ) v.x = eval(buf1);
|
||||
if( num > 1 ) v.y = eval(buf2);
|
||||
if( num > 2 ) v.z = eval(buf3);
|
||||
return v;
|
||||
}
|
||||
vec4 atof4(const char *s) {
|
||||
vec4 v = {0};
|
||||
char buf1[64],buf2[64],buf3[64],buf4[64];
|
||||
int num = sscanf(s, "%64[^]\r\n,}],%64[^]\r\n,}],%64[^]\r\n,}],%64[^]\r\n,}]", buf1, buf2, buf3, buf4);
|
||||
if( num > 0 ) v.x = eval(buf1);
|
||||
if( num > 1 ) v.y = eval(buf2);
|
||||
if( num > 2 ) v.z = eval(buf3);
|
||||
if( num > 3 ) v.w = eval(buf4);
|
||||
return v;
|
||||
}
|
||||
|
||||
// @todo: expand this to proper int parsers
|
||||
int atoi1(const char *s) {
|
||||
return (int)atof1(s);
|
||||
}
|
||||
vec2i atoi2(const char *s) {
|
||||
vec2 v = atof2(s);
|
||||
return vec2i( v.x, v.y );
|
||||
}
|
||||
vec3i atoi3(const char *s) {
|
||||
vec3 v = atof3(s);
|
||||
return vec3i( v.x, v.y, v.z );
|
||||
}
|
||||
|
||||
// endianness -----------------------------------------------------------------
|
||||
// - rlyeh, public domain
|
||||
|
||||
|
|
|
@ -1,3 +1,61 @@
|
|||
// -----------------------------------------------------------------------------
|
||||
// semantic versioning in a single byte (octal)
|
||||
// - rlyeh, public domain.
|
||||
//
|
||||
// - single octal byte that represents semantic versioning (major.minor.patch).
|
||||
// - allowed range [0000..0377] ( <-> [0..255] decimal )
|
||||
// - comparison checks only major.minor tuple as per convention.
|
||||
|
||||
API int semver( int major, int minor, int patch );
|
||||
API int semvercmp( int v1, int v2 );
|
||||
|
||||
#define SEMVER(major,minor,patch) (0100 * (major) + 010 * (minor) + (patch))
|
||||
#define SEMVERCMP(v1,v2) (((v1) & 0110) - ((v2) & 0110))
|
||||
#define SEMVERFMT "%03o"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// storage types. refer to vec2i/3i, vec2/3/4 if you plan to do math operations
|
||||
|
||||
typedef struct byte2 { uint8_t x,y; } byte2;
|
||||
typedef struct byte3 { uint8_t x,y,z; } byte3;
|
||||
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
|
||||
|
||||
typedef struct int2 { int x,y; } int2;
|
||||
typedef struct int3 { int x,y,z; } int3;
|
||||
typedef struct int4 { int x,y,z,w; } int4;
|
||||
|
||||
typedef struct uint2 { unsigned int x,y; } uint2;
|
||||
typedef struct uint3 { unsigned int x,y,z; } uint3;
|
||||
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
|
||||
|
||||
typedef struct float2 { float x,y; } float2;
|
||||
typedef struct float3 { float x,y,z; } float3;
|
||||
typedef struct float4 { float x,y,z,w; } float4;
|
||||
|
||||
typedef struct double2 { double x,y; } double2;
|
||||
typedef struct double3 { double x,y,z; } double3;
|
||||
typedef struct double4 { double x,y,z,w; } double4;
|
||||
|
||||
#define byte2(x,y) M_CAST(byte2, (uint8_t)(x), (uint8_t)(y) )
|
||||
#define byte3(x,y,z) M_CAST(byte3, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z) )
|
||||
#define byte4(x,y,z,w) M_CAST(byte4, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z), (uint8_t)(w) )
|
||||
|
||||
#define int2(x,y) M_CAST(int2, (int)(x), (int)(y) )
|
||||
#define int3(x,y,z) M_CAST(int3, (int)(x), (int)(y), (int)(z) )
|
||||
#define int4(x,y,z,w) M_CAST(int4, (int)(x), (int)(y), (int)(z), (int)(w) )
|
||||
|
||||
#define uint2(x,y) M_CAST(uint2, (unsigned)(x), (unsigned)(y) )
|
||||
#define uint3(x,y,z) M_CAST(uint3, (unsigned)(x), (unsigned)(y), (unsigned)(z) )
|
||||
#define uint4(x,y,z,w) M_CAST(uint4, (unsigned)(x), (unsigned)(y), (unsigned)(z), (unsigned)(w) )
|
||||
|
||||
#define float2(x,y) M_CAST(float2, (float)(x), (float)(y) )
|
||||
#define float3(x,y,z) M_CAST(float3, (float)(x), (float)(y), (float)(z) )
|
||||
#define float4(x,y,z,w) M_CAST(float4, (float)(x), (float)(y), (float)(z), (float)(w) )
|
||||
|
||||
#define double2(x,y) M_CAST(double2, (double)(x), (double)(y) )
|
||||
#define double3(x,y,z) M_CAST(double3, (double)(x), (double)(y), (double)(z) )
|
||||
#define double4(x,y,z,w) M_CAST(double4, (double)(x), (double)(y), (double)(z), (double)(w) )
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// compile-time fourcc, eightcc
|
||||
|
||||
|
@ -26,16 +84,25 @@ enum {
|
|||
#define cc7(a,b,c,d,e,f,g) cc8(,a,b,c,d,e,f,g)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// float conversion (text)
|
||||
// text conversions
|
||||
|
||||
API char* ftoa1(float v);
|
||||
API char* ftoa2(vec2 v);
|
||||
API char* ftoa3(vec3 v);
|
||||
API char* ftoa4(vec4 v);
|
||||
|
||||
API float atof1(const char *s);
|
||||
API vec2 atof2(const char *s);
|
||||
API vec3 atof3(const char *s);
|
||||
API vec4 atof4(const char *s);
|
||||
|
||||
API char* ftoa(float f);
|
||||
API char* ftoa2(vec2 v);
|
||||
API char* ftoa3(vec3 v);
|
||||
API char* ftoa4(vec4 v);
|
||||
API char* itoa1(int v);
|
||||
API char* itoa2(vec2i v);
|
||||
API char* itoa3(vec3i v);
|
||||
|
||||
API int atoi1(const char *s);
|
||||
API vec2i atoi2(const char *s);
|
||||
API vec3i atoi3(const char *s);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// endianness
|
||||
|
|
|
@ -14,33 +14,53 @@ AUTORUN {
|
|||
reflect_init();
|
||||
}
|
||||
|
||||
void type_inscribe(const char *TY,unsigned TYid,unsigned TYsz,const char *infos) {
|
||||
static
|
||||
const char* symbol(const char *s) {
|
||||
if( strbeg(s, "const ") ) s += 6;
|
||||
if( strbeg(s, "union ") ) s += 6;
|
||||
if( strbeg(s, "struct ") ) s += 7;
|
||||
if(!strstr(s, " *") ) return s;
|
||||
char *copy = va("%s", s);
|
||||
do strswap(copy," *","*"); while( strstr(copy, " *") ); // char * -> char*
|
||||
return (const char *)copy;
|
||||
}
|
||||
|
||||
void type_inscribe(const char *TY,unsigned TYsz,const char *infos) {
|
||||
reflect_init();
|
||||
unsigned TYid = intern(TY = symbol(TY));
|
||||
map_find_or_add(reflects, TYid, ((reflect_t){TYid, 0, TYsz, TY, infos}));
|
||||
}
|
||||
void enum_inscribe(const char *E,unsigned Eid,unsigned Eval,const char *infos) {
|
||||
void enum_inscribe(const char *E,unsigned Eval,const char *infos) {
|
||||
reflect_init();
|
||||
unsigned Eid = intern(E = symbol(E));
|
||||
map_find_or_add(reflects, Eid, ((reflect_t){Eid,0, Eval, E,infos}));
|
||||
}
|
||||
unsigned enum_find(const char *E) {
|
||||
reflect_init();
|
||||
E = symbol(E);
|
||||
return map_find(reflects, intern(E))->sz;
|
||||
}
|
||||
void function_inscribe(const char *F,unsigned Fid,void *func,const char *infos) {
|
||||
void function_inscribe(const char *F,void *func,const char *infos) {
|
||||
reflect_init();
|
||||
unsigned Fid = intern(F = symbol(F));
|
||||
map_find_or_add(reflects, Fid, ((reflect_t){Fid,0, 0, F,infos, func}));
|
||||
reflect_t *found = map_find(reflects,Fid);
|
||||
}
|
||||
void *function_find(const char *F) {
|
||||
reflect_init();
|
||||
F = symbol(F);
|
||||
return map_find(reflects, intern(F))->addr;
|
||||
}
|
||||
void struct_inscribe(const char *T,unsigned Tid,unsigned Tsz,unsigned OBJTYPEid, const char *infos) {
|
||||
void struct_inscribe(const char *T,unsigned Tsz,unsigned OBJTYPEid, const char *infos) {
|
||||
reflect_init();
|
||||
unsigned Tid = intern(T = symbol(T));
|
||||
map_find_or_add(reflects, Tid, ((reflect_t){Tid, OBJTYPEid, Tsz, T, infos}));
|
||||
}
|
||||
void member_inscribe(unsigned Tid, const char *M,unsigned Mid,unsigned Msz, const char *infos, const char *type, unsigned bytes) {
|
||||
void member_inscribe(const char *T, const char *M,unsigned Msz, const char *infos, const char *type, unsigned bytes) {
|
||||
reflect_init();
|
||||
unsigned Tid = intern(T = symbol(T));
|
||||
unsigned Mid = intern(M = symbol(M));
|
||||
type = symbol(type);
|
||||
map_find_or_add(reflects, (Mid<<16)|Tid, ((reflect_t){Mid, 0, Msz, M, infos, NULL, Tid, type }));
|
||||
// add member separately as well
|
||||
if(!members) map_init_int(members);
|
||||
|
@ -49,15 +69,20 @@ void member_inscribe(unsigned Tid, const char *M,unsigned Mid,unsigned Msz, cons
|
|||
}
|
||||
reflect_t member_find(const char *T, const char *M) {
|
||||
reflect_init();
|
||||
T = symbol(T);
|
||||
M = symbol(M);
|
||||
return *map_find(reflects, (intern(M)<<16)|intern(T));
|
||||
}
|
||||
void *member_findptr(void *obj, const char *T, const char *M) {
|
||||
reflect_init();
|
||||
T = symbol(T);
|
||||
M = symbol(M);
|
||||
return (char*)obj + member_find(T,M).sz;
|
||||
}
|
||||
array(reflect_t) members_find(const char *T) {
|
||||
array(reflect_t)* members_find(const char *T) {
|
||||
reflect_init();
|
||||
return *map_find(members, intern(T));
|
||||
T = symbol(T);
|
||||
return map_find(members, intern(T));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -26,15 +26,15 @@ typedef struct reflect_t {
|
|||
// inscribe api
|
||||
|
||||
#define ENUM(V, .../*value_annotations*/) \
|
||||
enum_inscribe(#V,intern(#V),V, "" __VA_ARGS__/*value_annotations*/)
|
||||
enum_inscribe(#V,V, "" __VA_ARGS__/*value_annotations*/)
|
||||
|
||||
#define FUNCTION(F, .../*function_annotations*/) \
|
||||
function_inscribe(#F,intern(#F),(void*)F, "" __VA_ARGS__/*function_annotations*/)
|
||||
function_inscribe(#F,(void*)F, "" __VA_ARGS__/*function_annotations*/)
|
||||
|
||||
#define STRUCT(T, type, member, .../*member_annotations*/) \
|
||||
struct_inscribe(#T,intern(#T),sizeof(T),OBJTYPE(T),NULL), \
|
||||
type_inscribe(#type,intern(#type),sizeof(((T){0}).member),"" __VA_ARGS__/*member_annotations*/), \
|
||||
member_inscribe(intern(#T), #member,intern(#member),(uintptr_t)&((T*)0)->member, "" __VA_ARGS__/*member_annotations*/, #type, sizeof(((T){0}).member) )
|
||||
struct_inscribe(#T,sizeof(T),OBJTYPE(T),NULL), \
|
||||
type_inscribe(#type,sizeof(((T){0}).member),"" __VA_ARGS__/*member_annotations*/), \
|
||||
member_inscribe(#T, #member,(uintptr_t)&((T*)0)->member, "" __VA_ARGS__/*member_annotations*/, #type, sizeof(((T){0}).member) )
|
||||
|
||||
// find api
|
||||
|
||||
|
@ -43,22 +43,22 @@ API void * function_find(const char *F);
|
|||
|
||||
API reflect_t member_find(const char *T, const char *M); /// find specific member
|
||||
API void * member_findptr(void *obj, const char *T, const char *M); // @deprecate
|
||||
API array(reflect_t) members_find(const char *T);
|
||||
API array(reflect_t)* members_find(const char *T);
|
||||
|
||||
// iterate members in a struct
|
||||
|
||||
#define each_member(T,R) \
|
||||
(array(reflect_t)*found_ = map_find(members, intern(T)); found_; found_ = 0) \
|
||||
(array(reflect_t) *found_ = members_find(T); found_; found_ = 0) \
|
||||
for(int it_ = 0, end_ = array_count(*found_); it_ != end_; ++it_ ) \
|
||||
for(reflect_t *R = (*found_)+it_; R; R = 0 )
|
||||
for(reflect_t *R = &(*found_)[it_]; R; R = 0 )
|
||||
|
||||
// private api, still exposed
|
||||
|
||||
API void type_inscribe(const char *TY,unsigned TYid,unsigned TYsz,const char *infos);
|
||||
API void enum_inscribe(const char *E,unsigned Eid,unsigned Eval,const char *infos);
|
||||
API void struct_inscribe(const char *T,unsigned Tid,unsigned Tsz,unsigned OBJTYPEid, const char *infos);
|
||||
API void member_inscribe(unsigned Tid, const char *M,unsigned Mid,unsigned Msz, const char *infos, const char *type, unsigned bytes);
|
||||
API void function_inscribe(const char *F,unsigned Fid,void *func,const char *infos);
|
||||
API void type_inscribe(const char *TY,unsigned TYsz,const char *infos);
|
||||
API void enum_inscribe(const char *E,unsigned Eval,const char *infos);
|
||||
API void struct_inscribe(const char *T,unsigned Tsz,unsigned OBJTYPEid, const char *infos);
|
||||
API void member_inscribe(const char *T, const char *M,unsigned Msz, const char *infos, const char *type, unsigned bytes);
|
||||
API void function_inscribe(const char *F,void *func,const char *infos);
|
||||
|
||||
API void reflect_print(const char *symbol);
|
||||
API void reflect_dump(const char *mask);
|
||||
|
|
|
@ -533,13 +533,13 @@ void shader_colormap(const char *name, colormap_t c ) {
|
|||
// colors
|
||||
|
||||
unsigned rgba( uint8_t r, uint8_t g, uint8_t b, uint8_t a ) {
|
||||
return (unsigned)r << 24 | g << 16 | b << 8 | a;
|
||||
return (unsigned)a << 24 | b << 16 | g << 8 | r;
|
||||
}
|
||||
unsigned bgra( uint8_t b, uint8_t g, uint8_t r, uint8_t a ) {
|
||||
return rgba(r,g,b,a);
|
||||
}
|
||||
float alpha( unsigned rgba ) {
|
||||
return ( rgba >> 24 ) / 255.f;
|
||||
unsigned alpha( unsigned rgba ) {
|
||||
return rgba >> 24;
|
||||
}
|
||||
|
||||
unsigned rgbaf(float r, float g, float b, float a) {
|
||||
|
|
|
@ -15,7 +15,7 @@ API unsigned rgba( uint8_t r, uint8_t g, uint8_t b, uint8_t a );
|
|||
API unsigned bgra( uint8_t b, uint8_t g, uint8_t r, uint8_t a );
|
||||
API unsigned rgbaf( float r, float g, float b, float a );
|
||||
API unsigned bgraf( float b, float g, float r, float a );
|
||||
API float alpha( unsigned rgba );
|
||||
API unsigned alpha( unsigned rgba );
|
||||
|
||||
#define RGBX(rgb,x) ( ((rgb)&0xFFFFFF) | (((unsigned)(x))<<24) )
|
||||
#define RGB3(r,g,b) ( (255<<24) | ((r)<<16) | ((g)<<8) | (b) )
|
||||
|
|
|
@ -79,13 +79,13 @@ API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
|
|||
// ## string interning (quarks)
|
||||
// - rlyeh, public domain.
|
||||
|
||||
unsigned intern( const char *string );
|
||||
const char *quark( unsigned key );
|
||||
API unsigned intern( const char *string );
|
||||
API const char *quark( unsigned key );
|
||||
|
||||
typedef struct quarks_db {
|
||||
array(char) blob;
|
||||
array(vec2i) entries;
|
||||
} quarks_db;
|
||||
|
||||
unsigned quark_intern( quarks_db*, const char *string );
|
||||
const char *quark_string( quarks_db*, unsigned key );
|
||||
API unsigned quark_intern( quarks_db*, const char *string );
|
||||
API const char *quark_string( quarks_db*, unsigned key );
|
||||
|
|
|
@ -279,7 +279,7 @@ void trap_on_abort(int signal) {
|
|||
exit(-1);
|
||||
}
|
||||
void trap_on_debug(int signal) {
|
||||
breakpoint("Error: unexpected signal");
|
||||
alert("Error: unexpected signal"), breakpoint();
|
||||
fprintf(stderr, "Error: unexpected signal %s (%d)\n%s\n", trap_name(signal), signal, callstack(16));
|
||||
exit(-1);
|
||||
}
|
||||
|
@ -549,7 +549,7 @@ void tty_color(unsigned color) {
|
|||
}
|
||||
#endif
|
||||
if( color ) {
|
||||
// if( color == RED ) breakpoint("break on RED"); // debug
|
||||
// if( color == RED ) alert("break on error message (RED)"), breakpoint(); // debug
|
||||
unsigned r = (color >> 16) & 255;
|
||||
unsigned g = (color >> 8) & 255;
|
||||
unsigned b = (color >> 0) & 255;
|
||||
|
@ -668,6 +668,9 @@ static void debugbreak(void) { // break if debugger present
|
|||
#endif
|
||||
|
||||
void alert(const char *message) { // @todo: move to app_, besides die()
|
||||
window_visible(false);
|
||||
message = message[0] == '!' ? (const char*)va("%s\n%s", message+1, callstack(+48)) : message;
|
||||
|
||||
#if is(win32)
|
||||
MessageBoxA(0, message, 0,0);
|
||||
#elif is(ems)
|
||||
|
@ -678,20 +681,14 @@ void alert(const char *message) { // @todo: move to app_, besides die()
|
|||
#elif is(osx)
|
||||
system(va("osascript -e 'display alert \"Alert\" message \"%s\"'", message));
|
||||
#endif
|
||||
}
|
||||
|
||||
void breakpoint(const char *reason) {
|
||||
window_visible(false);
|
||||
if( reason ) {
|
||||
const char *fulltext = reason[0] == '!' ? va("%s\n%s", reason+1, callstack(+48)) : reason;
|
||||
PRINTF("%s", fulltext);
|
||||
|
||||
(alert)(fulltext);
|
||||
}
|
||||
debugbreak();
|
||||
window_visible(true);
|
||||
}
|
||||
|
||||
void breakpoint() {
|
||||
debugbreak();
|
||||
}
|
||||
|
||||
bool has_debugger() {
|
||||
#if is(win32)
|
||||
return IsDebuggerPresent(); // SetLastError(123); OutputDebugStringA("\1"); enabled = GetLastError() != 123;
|
||||
|
@ -752,7 +749,9 @@ int (PANIC)(const char *error, const char *file, int line) {
|
|||
|
||||
tty_color(0);
|
||||
|
||||
breakpoint(error);
|
||||
alert(error);
|
||||
breakpoint();
|
||||
|
||||
exit(-line);
|
||||
return 1;
|
||||
}
|
||||
|
@ -793,11 +792,37 @@ void app_hang() {
|
|||
for(;;);
|
||||
}
|
||||
void app_crash() {
|
||||
int *p = 0;
|
||||
volatile int *p = 0;
|
||||
*p = 42;
|
||||
}
|
||||
void app_beep() {
|
||||
ifdef(win32, system("rundll32 user32.dll,MessageBeep"); return; );
|
||||
ifdef(linux, system("paplay /usr/share/sounds/freedesktop/stereo/message.oga"); return; );
|
||||
ifdef(osx, system("tput bel"); return; );
|
||||
|
||||
//fallback:
|
||||
fputc('\x7', stdout);
|
||||
|
||||
// win32:
|
||||
// rundll32 user32.dll,MessageBeep ; ok
|
||||
// rundll32 cmdext.dll,MessageBeepStub ; ok
|
||||
|
||||
// osx:
|
||||
// tput bel
|
||||
// say "beep"
|
||||
// osascript -e 'beep'
|
||||
// osascript -e "beep 1"
|
||||
// afplay /System/Library/Sounds/Ping.aiff
|
||||
// /usr/bin/printf "\a"
|
||||
|
||||
// linux:
|
||||
// paplay /usr/share/sounds/freedesktop/stereo/message.oga ; ok
|
||||
// paplay /usr/share/sounds/freedesktop/stereo/complete.oga ; ok
|
||||
// paplay /usr/share/sounds/freedesktop/stereo/bell.oga ; ok
|
||||
// beep ; apt-get
|
||||
// echo -e '\007' ; mute
|
||||
// echo -e "\007" >/dev/tty10 ; sudo
|
||||
// tput bel ; mute
|
||||
}
|
||||
|
||||
void app_singleton(const char *guid) {
|
||||
|
@ -878,10 +903,12 @@ const char* app_savefile() {
|
|||
// ----------------------------------------------------------------------------
|
||||
// tests
|
||||
|
||||
static __thread int test_oks, test_errs, test_once;
|
||||
static void test_exit(void) { fprintf(stderr, "%d/%d tests passed\n", test_oks, test_oks+test_errs); }
|
||||
static __thread int test_oks, test_errors, test_once;
|
||||
static void test_exit(void) { fprintf(stderr, "%d/%d tests passed\n", test_oks, test_oks+test_errors); }
|
||||
int (test)(const char *file, int line, const char *expr, bool result) {
|
||||
static int breakon = -1; if(breakon<0) breakon = optioni("--test-break", 0);
|
||||
if( breakon == (test_oks+test_errors+1) ) alert("user requested to break on this test"), breakpoint();
|
||||
test_once = test_once || !(atexit)(test_exit);
|
||||
test_oks += result, test_errs += !result;
|
||||
test_oks += result, test_errors += !result;
|
||||
return (result || (tty_color(RED), fprintf(stderr, "(Test `%s` failed %s:%d)\n", expr, file, line), tty_color(0), 0) );
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ API void die(const char *message);
|
|||
API void alert(const char *message);
|
||||
API void hexdump( const void *ptr, unsigned len );
|
||||
API void hexdumpf( FILE *fp, const void *ptr, unsigned len, int width );
|
||||
API void breakpoint(const char *optional_reason);
|
||||
API void breakpoint();
|
||||
API bool has_debugger();
|
||||
|
||||
API void trap_install(void);
|
||||
|
|
|
@ -24,7 +24,7 @@ API void timer_destroy(unsigned timer_handle);
|
|||
//
|
||||
// also similar to a mongo object id, 12 bytes as follows:
|
||||
// - 4-byte timestamp (ss). epoch: Tuesday, 12 September 2023 6:06:56
|
||||
// - 2-byte (machine or app hash)
|
||||
// - 2-byte (machine, hash or app id)
|
||||
// - 2-byte (thread-id)
|
||||
// - 4-byte (rand counter, that gets increased at every id creation)
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#define UI_FONT_ENUM(carlito,b612) b612 // carlito
|
||||
|
||||
#define UI_FONT_ICONS "MaterialIconsSharp-Regular.otf" // "MaterialIconsOutlined-Regular.otf" "MaterialIcons-Regular.ttf" //
|
||||
#define UI_FONT_REGULAR UI_FONT_ENUM("Carlito", "B612") "-Regular.ttf"
|
||||
#define UI_FONT_HEADING UI_FONT_ENUM("Carlito", "B612") "-BoldItalic.ttf"
|
||||
#define UI_FONT_TERMINAL UI_FONT_ENUM("Inconsolata", "B612Mono") "-Regular.ttf"
|
||||
|
@ -76,12 +75,16 @@ static void nk_config_custom_fonts() {
|
|||
}
|
||||
|
||||
// ...with icons embedded on it.
|
||||
|
||||
for( char *data = vfs_load(UI_FONT_ICONS, &datalen); data; data = 0 ) {
|
||||
static const nk_rune icon_range[] = {UI_ICON_MIN, UI_ICON_MED /*MAX*/, 0};
|
||||
|
||||
static struct icon_font {
|
||||
const char *file; nk_rune range[3];
|
||||
} icons[] = {
|
||||
{"MaterialIconsSharp-Regular.otf", {UI_ICON_MIN, UI_ICON_MED /*MAX*/, 0}}, // "MaterialIconsOutlined-Regular.otf" "MaterialIcons-Regular.ttf"
|
||||
{"materialdesignicons-webfont.ttf", {0xF68C /*ICON_MIN_MDI*/, 0xF1C80/*ICON_MAX_MDI*/, 0}},
|
||||
};
|
||||
for( int f = 0; f < countof(icons); ++f )
|
||||
for( char *data = vfs_load(icons[f].file, &datalen); data; data = 0 ) {
|
||||
struct nk_font_config cfg = nk_font_config(UI_ICON_FONTSIZE);
|
||||
cfg.range = icon_range; // nk_font_default_glyph_ranges();
|
||||
cfg.range = icons[f].range; // nk_font_default_glyph_ranges();
|
||||
cfg.merge_mode = 1;
|
||||
|
||||
cfg.spacing.x += UI_ICON_SPACING_X;
|
||||
|
@ -1596,6 +1599,8 @@ int ui_label2_toolbar(const char *label, const char *icons) {
|
|||
}
|
||||
|
||||
int ui_notify(const char *title, const char *body) {
|
||||
app_beep();
|
||||
|
||||
struct ui_notify n = {0};
|
||||
n.title = title && title[0] ? stringf("*%s", title) : 0;
|
||||
n.body = body && body[0] ? STRDUP(body) : 0;
|
||||
|
|
|
@ -271,7 +271,8 @@ void glNewFrame() {
|
|||
}
|
||||
|
||||
bool window_create_from_handle(void *handle, float scale, unsigned flags) {
|
||||
ifdef(debug, if( flag("--tests") ) exit(0));
|
||||
// abort run if any test suite failed in unit-test mode
|
||||
ifdef(debug, if( flag("--test-only") ) exit( test_errors ? -test_errors : 0 ));
|
||||
|
||||
glfw_init();
|
||||
v4k_init();
|
||||
|
|
502
engine/v4k
502
engine/v4k
|
@ -199014,7 +199014,7 @@ nk_utf_decode(const char *c, nk_rune *u, int clen)
|
|||
*u = NK_UTF_INVALID;
|
||||
|
||||
udecoded = nk_utf_decode_byte(c[0], &len);
|
||||
if (!NK_BETWEEN(len, 1, NK_UTF_SIZE))
|
||||
if (!NK_BETWEEN(len, 1, NK_UTF_SIZE+1)) //< @r-lyeh: add +1 for len<=4
|
||||
return 1;
|
||||
|
||||
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
|
||||
|
@ -211149,7 +211149,12 @@ window->is_window_resizing |= layout->flags & NK_WINDOW_SCALE_TOP ? NK_WINDOW_SC
|
|||
}
|
||||
|
||||
}
|
||||
ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT];
|
||||
int icon = //< @r-lyeh
|
||||
((layout->flags & NK_WINDOW_SCALE_TOP) && !(layout->flags & NK_WINDOW_SCALE_LEFT))
|
||||
||
|
||||
((layout->flags & NK_WINDOW_SCALE_LEFT) && !(layout->flags & NK_WINDOW_SCALE_TOP))
|
||||
? NK_CURSOR_RESIZE_TOP_LEFT_DOWN_RIGHT : NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT;
|
||||
ctx->style.cursor_active = ctx->style.cursors[icon]; //< @r-lyeh
|
||||
in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = scaler.x + scaler.w/2.0f;
|
||||
in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = scaler.y + scaler.h/2.0f;
|
||||
}
|
||||
|
@ -312982,6 +312987,499 @@ API void ProgressiveMesh(int vert_n, int vert_stride, const float *v, int tri_n,
|
|||
* SOFTWARE.
|
||||
*/
|
||||
#line 0
|
||||
#define expr expr2 // 3rd_lua.h
|
||||
#line 1 "engine/split/3rd_eval.h"
|
||||
/* A mathematical expression evaluator.
|
||||
* It uses a recursive descent parser internally.
|
||||
* Author: Werner Stoop
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
* http://unlicense.org/
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h> /* remember to compile with -lm */
|
||||
#include <setjmp.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Special tokens used by the lexer function lex()
|
||||
* they've been chosen as non-printable characters
|
||||
* so that printable characters can be used for other
|
||||
* purposes
|
||||
*/
|
||||
#define TOK_END 0 /* end of text */
|
||||
#define TOK_INI 1 /* Initial state */
|
||||
#define TOK_ID 2 /* identifier */
|
||||
#define TOK_NUM 3 /* number */
|
||||
|
||||
/* Types of errors */
|
||||
// 0 /* "no error" */
|
||||
#define ERR_MEMORY 1 /* "out of memory" */
|
||||
#define ERR_LEXER 2 /* "unknown token" */
|
||||
#define ERR_LONGID 3 /* "identifier too long" */
|
||||
#define ERR_VALUE 4 /* "value expected" */
|
||||
#define ERR_BRACKET 5 /* "missing ')'" */
|
||||
#define ERR_FUNC 6 /* "unknown function" */
|
||||
#define ERR_ARGS 7 /* "wrong number of arguments" */
|
||||
#define ERR_CONST 8 /* "unknown constant" */
|
||||
|
||||
/* Other definitions */
|
||||
#define MAX_ID_LEN 11 /* Max length of an identifier */
|
||||
#define OPERATORS "+-*/%(),^" /* Valid operators */
|
||||
|
||||
#define EVAL_PI 3.141592654
|
||||
#define EVAL_E 2.718281828
|
||||
#define EVAL_DEG (EVAL_PI/180)
|
||||
|
||||
/* Internal structure for the parser/evaluator */
|
||||
struct eval {
|
||||
|
||||
jmp_buf j; /* For error handling */
|
||||
|
||||
const char *p; /* Position in the text being parsed */
|
||||
|
||||
double *st; /* Stack */
|
||||
int st_size; /* Stack size */
|
||||
int sp; /* Stack pointer */
|
||||
|
||||
/* The current and next tokens identified by the lexer */
|
||||
struct {
|
||||
int type; /* Type of the token */
|
||||
double n_val; /* Numeric value of the previous lexed token */
|
||||
char s_val[MAX_ID_LEN]; /* String (identifier) value of the previous lexed token */
|
||||
} token[2];
|
||||
|
||||
int cur_tok; /* Current token, either 0 or 1 (see the comments of lex()) */
|
||||
};
|
||||
|
||||
/* Prototypes */
|
||||
static double pop(struct eval *ev);
|
||||
static void push(struct eval *ev, double d);
|
||||
static int lex(struct eval *ev);
|
||||
|
||||
/* Prototypes for the recursive descent parser */
|
||||
static void expr(struct eval *ev);
|
||||
static void add_expr(struct eval *ev);
|
||||
static void mul_expr(struct eval *ev);
|
||||
static void pow_expr(struct eval *ev);
|
||||
static void uni_expr(struct eval *ev);
|
||||
static void bra_expr(struct eval *ev);
|
||||
static void id_expr(struct eval *ev);
|
||||
static void num_expr(struct eval *ev);
|
||||
|
||||
/*
|
||||
* Evaluates a mathemeatical expression
|
||||
*/
|
||||
double eval(const char *exp/*, int *ep*/) {
|
||||
int _ep, *ep = &_ep;
|
||||
struct eval ev;
|
||||
double ans = 0.0;
|
||||
|
||||
assert(ep != NULL);
|
||||
|
||||
/* Allocate a stack */
|
||||
ev.st_size = 10;
|
||||
ev.st = CALLOC(ev.st_size, sizeof *ev.st);
|
||||
if(!ev.st)
|
||||
{
|
||||
*ep = ERR_MEMORY;
|
||||
return NAN; //0.0;
|
||||
}
|
||||
ev.sp = 0;
|
||||
|
||||
/* Manage errors */
|
||||
*ep = setjmp(ev.j);
|
||||
if(*ep != 0)
|
||||
{
|
||||
FREE(ev.st);
|
||||
return NAN; //0.0;
|
||||
}
|
||||
|
||||
/* Initialize the lexer */
|
||||
ev.token[0].type = TOK_INI;
|
||||
ev.token[0].s_val[0] = '\0';
|
||||
ev.token[1].type = TOK_INI;
|
||||
ev.token[1].s_val[0] = '\0';
|
||||
ev.cur_tok = 0;
|
||||
|
||||
/* Initialize the parser */
|
||||
ev.p = exp;
|
||||
|
||||
/* lex once to initialize the lexer */
|
||||
if(lex(&ev) != TOK_END)
|
||||
{
|
||||
expr(&ev);
|
||||
ans = pop(&ev);
|
||||
}
|
||||
|
||||
FREE(ev.st);
|
||||
return ans;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pushes a value onto the stack, increases the stack size if necessary
|
||||
*/
|
||||
static void push(struct eval *ev, double d) {
|
||||
if(ev->sp == ev->st_size) {
|
||||
/* Resize the stack by 1.5 */
|
||||
double *old = ev->st;
|
||||
int new_size = ev->st_size + (ev->st_size >> 1);
|
||||
ev->st = REALLOC(ev->st, new_size);
|
||||
if(!ev->st) {
|
||||
ev->st = old;
|
||||
longjmp(ev->j, ERR_MEMORY);
|
||||
}
|
||||
|
||||
ev->st_size = new_size;
|
||||
}
|
||||
|
||||
ev->st[ev->sp++] = d;
|
||||
}
|
||||
|
||||
// Pops a value from the top of the stack
|
||||
static double pop(struct eval *ev) {
|
||||
assert(ev->sp > 0);
|
||||
return ev->st[--ev->sp];
|
||||
}
|
||||
|
||||
// stricmp() is common, but not standard, so I provide my own
|
||||
static int istrcmp(const char *p, const char *q) {
|
||||
for(; tolower(p[0]) == tolower(q[0]) && p[0]; p++, q++);
|
||||
return tolower(p[0]) - tolower(q[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lexical analyzer function
|
||||
*
|
||||
* In order to implement LL(1), struct eval has an array of two token structures,
|
||||
* and its cur_tok member is used to point to the _current_ token, while the other
|
||||
* element contains the _next_ token. This implements a 2 element ring buffer where
|
||||
* the lexer always writes to the _next_ token so that the recursive descent parser can
|
||||
* _peek_ at the next token.
|
||||
*/
|
||||
|
||||
static int lex(struct eval *ev) {
|
||||
int next_tok;
|
||||
|
||||
start:
|
||||
/* Cycle the tokens */
|
||||
next_tok = ev->cur_tok;
|
||||
ev->cur_tok = ev->cur_tok?0:1;
|
||||
|
||||
while(isspace(ev->p[0])) ev->p++;
|
||||
|
||||
if(!ev->p[0]) {
|
||||
/* End of the expression */
|
||||
ev->token[next_tok].type = TOK_END;
|
||||
goto end;
|
||||
}
|
||||
else if(isdigit(ev->p[0]) || ev->p[0] == '.') {
|
||||
/* Number */
|
||||
char *endp;
|
||||
ev->token[next_tok].type = TOK_NUM;
|
||||
ev->token[next_tok].n_val = strtod(ev->p, &endp);
|
||||
ev->p = endp;
|
||||
goto end;
|
||||
}
|
||||
else if(isalpha(ev->p[0])) {
|
||||
/* Identifier */
|
||||
int i;
|
||||
for(i = 0; isalnum(ev->p[0]) && i < MAX_ID_LEN - 1; i++, ev->p++)
|
||||
ev->token[next_tok].s_val[i] = ev->p[0];
|
||||
|
||||
if(isalpha(ev->p[0])) longjmp(ev->j, ERR_LONGID);
|
||||
|
||||
ev->token[next_tok].s_val[i] = '\0';
|
||||
ev->token[next_tok].type = TOK_ID;
|
||||
goto end;
|
||||
}
|
||||
else if(strchr(OPERATORS, ev->p[0])) {
|
||||
/* Operator */
|
||||
ev->token[next_tok].type = ev->p[0];
|
||||
ev->p++;
|
||||
goto end;
|
||||
}
|
||||
else /* Unknown token */
|
||||
longjmp(ev->j, ERR_LEXER);
|
||||
|
||||
end:
|
||||
|
||||
/* If this was the first call, cycle the tokens again */
|
||||
if(ev->token[ev->cur_tok].type == TOK_INI)
|
||||
goto start;
|
||||
|
||||
return ev->token[ev->cur_tok].type;
|
||||
}
|
||||
|
||||
#define EVAL_TYPE(e) (e->token[e->cur_tok].type)
|
||||
#define EVAL_ERROR(c) longjmp(ev->j, (c))
|
||||
|
||||
// num_expr ::= NUMBER
|
||||
static void num_expr(struct eval *ev) {
|
||||
if(EVAL_TYPE(ev) != TOK_NUM)
|
||||
EVAL_ERROR(ERR_VALUE);
|
||||
push(ev, ev->token[ev->cur_tok].n_val);
|
||||
lex(ev);
|
||||
}
|
||||
|
||||
// expr ::= add_expr
|
||||
static void expr(struct eval *ev) {
|
||||
add_expr(ev);
|
||||
}
|
||||
|
||||
// add_expr ::= mul_expr [('+'|'-') mul_expr]
|
||||
static void add_expr(struct eval *ev) {
|
||||
int t;
|
||||
mul_expr(ev);
|
||||
while((t =EVAL_TYPE(ev)) == '+' || t == '-') {
|
||||
double a,b;
|
||||
lex(ev);
|
||||
mul_expr(ev);
|
||||
b = pop(ev);
|
||||
a = pop(ev);
|
||||
|
||||
if(t == '+')
|
||||
push(ev, a + b);
|
||||
else
|
||||
push(ev, a - b);
|
||||
}
|
||||
}
|
||||
|
||||
// mul_expr ::= pow_expr [('*'|'/'|'%') pow_expr]
|
||||
static void mul_expr(struct eval *ev) {
|
||||
int t;
|
||||
pow_expr(ev);
|
||||
while((t = EVAL_TYPE(ev)) == '*' || t == '/' || t == '%') {
|
||||
double a,b;
|
||||
lex(ev);
|
||||
pow_expr(ev);
|
||||
b = pop(ev);
|
||||
a = pop(ev);
|
||||
|
||||
if(t == '*')
|
||||
push(ev, a * b);
|
||||
else if(t == '/')
|
||||
push(ev, a / b);
|
||||
else
|
||||
push(ev, fmod(a, b));
|
||||
}
|
||||
}
|
||||
|
||||
// pow_expr ::= uni_expr ['^' pow_expr]
|
||||
static void pow_expr(struct eval *ev) {
|
||||
/* Note that exponentiation is right associative:
|
||||
2^3^4 is 2^(3^4), not (2^3)^4 */
|
||||
uni_expr(ev);
|
||||
if(EVAL_TYPE(ev) == '^') {
|
||||
double a,b;
|
||||
lex(ev);
|
||||
pow_expr(ev);
|
||||
b = pop(ev);
|
||||
a = pop(ev);
|
||||
push(ev, pow(a,b));
|
||||
}
|
||||
}
|
||||
|
||||
// uni_expr ::= ['+'|'-'] bra_expr
|
||||
static void uni_expr(struct eval *ev) {
|
||||
int t = '+';
|
||||
if(EVAL_TYPE(ev) == '-' || EVAL_TYPE(ev) == '+') {
|
||||
t = EVAL_TYPE(ev);
|
||||
lex(ev);
|
||||
}
|
||||
|
||||
bra_expr(ev);
|
||||
|
||||
if(t == '-') {
|
||||
double a = pop(ev);
|
||||
push(ev, -a);
|
||||
}
|
||||
}
|
||||
|
||||
// bra_expr ::= '(' add_expr ')' | id_expr
|
||||
static void bra_expr(struct eval *ev) {
|
||||
if(EVAL_TYPE(ev) == '(') {
|
||||
lex(ev);
|
||||
add_expr(ev);
|
||||
if(EVAL_TYPE(ev) != ')')
|
||||
EVAL_ERROR(ERR_BRACKET);
|
||||
lex(ev);
|
||||
}
|
||||
else
|
||||
id_expr(ev);
|
||||
}
|
||||
|
||||
// id_expr ::= ID '(' add_expr [',' add_expr]* ')' | ID | num_expr
|
||||
static void id_expr(struct eval *ev) {
|
||||
int nargs = 0;
|
||||
char id[MAX_ID_LEN];
|
||||
if(EVAL_TYPE(ev) != TOK_ID) {
|
||||
num_expr(ev);
|
||||
} else {
|
||||
strcpy(id, ev->token[ev->cur_tok].s_val);
|
||||
lex(ev);
|
||||
if(EVAL_TYPE(ev) != '(') {
|
||||
/**/ if(!istrcmp(id, "true")) push(ev, 1.0);
|
||||
else if(!istrcmp(id, "false")) push(ev, 0.0);
|
||||
else if(!istrcmp(id, "on")) push(ev, 1.0);
|
||||
else if(!istrcmp(id, "off")) push(ev, 0.0);
|
||||
// pi - 3.141592654
|
||||
else if(!istrcmp(id, "pi"))
|
||||
push(ev, EVAL_PI);
|
||||
// e - base of natural logarithms, 2.718281828
|
||||
else if(!istrcmp(id, "e"))
|
||||
push(ev, EVAL_E);
|
||||
// deg - deg2rad, allows to degree conversion `sin(90*deg) = 1`
|
||||
else if(!istrcmp(id, "deg"))
|
||||
push(ev, EVAL_DEG);
|
||||
else
|
||||
EVAL_ERROR(ERR_CONST);
|
||||
} else {
|
||||
lex(ev);
|
||||
|
||||
while(EVAL_TYPE(ev) != ')') {
|
||||
add_expr(ev);
|
||||
nargs++;
|
||||
if(EVAL_TYPE(ev) == ')') break;
|
||||
|
||||
if(EVAL_TYPE(ev) != ',')
|
||||
EVAL_ERROR(ERR_BRACKET);
|
||||
lex(ev);
|
||||
}
|
||||
lex(ev);
|
||||
|
||||
// abs(x) - absolute value of x
|
||||
if(!istrcmp(id, "abs")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, fabs(pop(ev)));
|
||||
}
|
||||
// ceil(x) - smallest integer greater than x
|
||||
else if(!istrcmp(id, "ceil")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, ceil(pop(ev)));
|
||||
}
|
||||
// floor(x) - largest integer smaller than x
|
||||
else if(!istrcmp(id, "floor")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, floor(pop(ev)));
|
||||
}
|
||||
// sin(x) - sine of x, in radians
|
||||
else if(!istrcmp(id, "sin")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, sin(pop(ev)));
|
||||
}
|
||||
// asin(x) - arcsine of x, in radians
|
||||
else if(!istrcmp(id, "asin")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, asin(pop(ev)));
|
||||
}
|
||||
// cos(x) - cosine of x, in radians
|
||||
else if(!istrcmp(id, "cos")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, cos(pop(ev)));
|
||||
}
|
||||
// acos(x) - arccosine of x, in radians
|
||||
else if(!istrcmp(id, "acos")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, acos(pop(ev)));
|
||||
}
|
||||
// tan(x) - tangent of x, in radians
|
||||
else if(!istrcmp(id, "tan")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, tan(pop(ev)));
|
||||
}
|
||||
// atan(x) - arctangent of x, in radians
|
||||
else if(!istrcmp(id, "atan")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, atan(pop(ev)));
|
||||
}
|
||||
// atan(y,x) - arctangent of y/x, in radians.
|
||||
else if(!istrcmp(id, "atan2")) {
|
||||
double a, b;
|
||||
if(nargs != 2) EVAL_ERROR(ERR_ARGS);
|
||||
b = pop(ev);
|
||||
a = pop(ev);
|
||||
push(ev, atan2(a,b));
|
||||
}
|
||||
// sinh(x) - hyperbolic sine of x, in radians
|
||||
else if(!istrcmp(id, "sinh")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, sinh(pop(ev)));
|
||||
}
|
||||
// cosh(x) - hyperbolic cosine of x, in radians
|
||||
else if(!istrcmp(id, "cosh")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, cosh(pop(ev)));
|
||||
}
|
||||
// tanh(x) - hyperbolic tangent of x, in radians
|
||||
else if(!istrcmp(id, "tanh")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, tanh(pop(ev)));
|
||||
}
|
||||
// log(x) - natural logarithm of x
|
||||
else if(!istrcmp(id, "log")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, log(pop(ev)));
|
||||
}
|
||||
// log10(x) - logarithm of x, base-10
|
||||
else if(!istrcmp(id, "log10")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, log10(pop(ev)));
|
||||
}
|
||||
// exp(x) - computes e^x
|
||||
else if(!istrcmp(id, "exp")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, exp(pop(ev)));
|
||||
}
|
||||
// sqrt(x) - square root of x
|
||||
else if(!istrcmp(id, "sqrt")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, sqrt(pop(ev)));
|
||||
}
|
||||
// rad(x) - converts x from degrees to radians
|
||||
else if(!istrcmp(id, "rad")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, pop(ev)*EVAL_PI/180);
|
||||
}
|
||||
// deg(x) - converts x from radians to degrees
|
||||
else if(!istrcmp(id, "deg")) {
|
||||
if(nargs != 1) EVAL_ERROR(ERR_ARGS);
|
||||
push(ev, pop(ev)*180/EVAL_PI);
|
||||
}
|
||||
// pow(x,y) - computes x^y
|
||||
else if(!istrcmp(id, "pow")) {
|
||||
double a, b;
|
||||
if(nargs != 2) EVAL_ERROR(ERR_ARGS);
|
||||
b = pop(ev);
|
||||
a = pop(ev);
|
||||
push(ev, pow(a,b));
|
||||
}
|
||||
// hypot(x,y) - computes sqrt(x*x + y*y)
|
||||
else if(!istrcmp(id, "hypot")) {
|
||||
double a, b;
|
||||
if(nargs != 2) EVAL_ERROR(ERR_ARGS);
|
||||
b = pop(ev);
|
||||
a = pop(ev);
|
||||
push(ev, sqrt(a*a + b*b));
|
||||
}
|
||||
else
|
||||
EVAL_ERROR(ERR_FUNC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
#ifdef EVALDEMO
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
assert( eval("1+1") == 2 ); // common path
|
||||
assert( eval("1+") != eval("1+") ); // check that errors return NAN
|
||||
assert(~puts("Ok") );
|
||||
}
|
||||
#endif
|
||||
#line 0
|
||||
// #define SQLITE_OMIT_LOAD_EXTENSION
|
||||
// #define SQLITE_CORE 1
|
||||
// #define SQLITE_DEBUG 1
|
||||
|
|
1095
engine/v4k.c
1095
engine/v4k.c
File diff suppressed because it is too large
Load Diff
487
engine/v4k.h
487
engine/v4k.h
|
@ -267,6 +267,7 @@ extern "C" {
|
|||
#define conc4t(a,b) a##b ///-
|
||||
|
||||
#define macro(name) concat(name, __LINE__)
|
||||
#define unique(name) concat(concat(concat(name,concat(_L,__LINE__)),_),__COUNTER__)
|
||||
#define defer(begin,end) for(int macro(i) = ((begin), 0); !macro(i); macro(i) = ((end), 1))
|
||||
#define scope(end) defer((void)0, end)
|
||||
#define benchmark for(double macro(i) = 1, macro(t) = (time_ss(),-time_ss()); macro(i); macro(t)+=time_ss(), macro(i)=0, printf("%.4fs %2.f%% (" FILELINE ")\n", macro(t), macro(t)*100/0.0166667 ))
|
||||
|
@ -291,10 +292,10 @@ extern "C" {
|
|||
#define ASSERT(expr, ...) (void)0
|
||||
#define ASSERT_ONCE(expr, ...) (void)0
|
||||
#else
|
||||
#define ASSERT(expr, ...) do { int fool_msvc[] = {0,}; if(!(expr)) { fool_msvc[0]++; breakpoint(va("!Expression failed: " #expr " " FILELINE "\n" __VA_ARGS__)); } } while(0)
|
||||
#define ASSERT_ONCE(expr, ...) do { int fool_msvc[] = {0,}; if(!(expr)) { fool_msvc[0]++; static int seen = 0; if(!seen) seen = 1, breakpoint(va("!Expression failed: " #expr " " FILELINE "\n" __VA_ARGS__)); } } while(0)
|
||||
#define ASSERT(expr, ...) do { int fool_msvc[] = {0,}; if(!(expr)) { fool_msvc[0]++; alert(va("!Expression failed: " #expr " " FILELINE "\n" __VA_ARGS__)), breakpoint(); } } while(0)
|
||||
#define ASSERT_ONCE(expr, ...) do { int fool_msvc[] = {0,}; if(!(expr)) { fool_msvc[0]++; static int seen = 0; if(!seen) seen = 1, alert(va("!Expression failed: " #expr " " FILELINE "\n" __VA_ARGS__)), breakpoint(); } } while(0)
|
||||
#endif
|
||||
#define STATIC_ASSERT(EXPR) typedef struct { unsigned macro(static_assert_on_line_) : !!(EXPR); } macro(static_assert_on_line_)
|
||||
#define STATIC_ASSERT(EXPR) typedef struct { unsigned macro(static_assert_on_L) : !!(EXPR); } unique(static_assert_on_L)
|
||||
|
||||
#define FILELINE __FILE__ ":" STRINGIZE(__LINE__)
|
||||
#define STRINGIZE(x) STRINGIZ3(x)
|
||||
|
@ -371,6 +372,7 @@ extern "C" {
|
|||
// note: based on code by Joe Lowe (public domain).
|
||||
// note: XIU for C initializers, XCU for C++ initializers, XTU for C deinitializers
|
||||
|
||||
#define AUTORUN AUTORUN_( unique(fn) )
|
||||
#ifdef __cplusplus
|
||||
#define AUTORUN_(fn) \
|
||||
static void fn(void); \
|
||||
|
@ -384,14 +386,16 @@ extern "C" {
|
|||
__declspec(allocate(".CRT$XIU")) \
|
||||
static int(* concat(fn,__2) )() = concat(fn,__1); \
|
||||
static void fn(void)
|
||||
#else // gcc,tcc,clang,clang-cl...
|
||||
#elif defined __TINYC__ // tcc...
|
||||
#define AUTORUN_(fn) \
|
||||
__attribute__((constructor)) \
|
||||
static void fn(void)
|
||||
#else // gcc,clang,clang-cl...
|
||||
#define AUTORUN_(fn) \
|
||||
__attribute__((constructor(__COUNTER__+101))) \
|
||||
static void fn(void)
|
||||
#endif
|
||||
|
||||
#define AUTORUN AUTORUN_( concat(concat(concat(fn_L,__LINE__),_),__COUNTER__) )
|
||||
|
||||
#if 0 // autorun demo
|
||||
void byebye(void) { puts("seen after main()"); }
|
||||
AUTORUN { puts("seen before main()"); }
|
||||
|
@ -408,7 +412,7 @@ AUTORUN { puts("seen before main() too"); atexit( byebye ); }
|
|||
// -----------------------------------------------------------------------------
|
||||
// visibility
|
||||
|
||||
// win32 users would need to -DAPI=IMPORT/EXPORT as needed when using/building V4K as DLL.
|
||||
// win32 users would need to -DAPI=EXPORT/IMPORT as needed when building/using V4K as DLL.
|
||||
|
||||
#define IMPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllimport)), __declspec(dllimport)))
|
||||
#define EXPORT ifdef(win32, ifdef(gcc, __attribute__ ((dllexport)), __declspec(dllexport)))
|
||||
|
@ -581,7 +585,7 @@ static __thread unsigned array_n_;
|
|||
#define array_vlen_(t) ( vlen(t) - 0 )
|
||||
#define array_realloc_(t,n) ( (t) = array_cast(t) vrealloc((t), ((n)+0) * sizeof(0[t])) )
|
||||
#define array_free(t) array_clear(t)
|
||||
#else // new: with reserve support (bugs?)
|
||||
#else // new: with reserve support (@todo: check for bugs?)
|
||||
#define array_reserve(t, n) ( array_realloc_((t),(n)), array_clear(t) )
|
||||
#define array_clear(t) ( array_realloc_((t),0) ) // -1
|
||||
#define array_vlen_(t) ( vlen(t) - sizeof(0[t]) ) // -1
|
||||
|
@ -628,7 +632,7 @@ static __thread unsigned array_n_;
|
|||
memcpy( (t), src, array_count(src) * sizeof(0[t])); \
|
||||
} while(0)
|
||||
|
||||
#define array_erase_fast(t, i) do { /*may alter ordering*/ \
|
||||
#define array_erase_fast(t, i) do { /*alters ordering*/ \
|
||||
memcpy( &(t)[i], &(t)[array_count(t) - 1], sizeof(0[t])); \
|
||||
array_pop(t); \
|
||||
} while(0)
|
||||
|
@ -950,9 +954,9 @@ API void (map_clear)(map* m);
|
|||
// Credits: @ands+@krig+@vurtun (PD), @datenwolf (WTFPL2), @evanw+@barerose (CC0), @sgorsten (Unlicense).
|
||||
|
||||
#define C_EPSILON (1e-6)
|
||||
#define C_PI (3.141592654f) // (3.14159265358979323846f)
|
||||
#define TO_RAD (C_PI/180.f)
|
||||
#define TO_DEG (180.f/C_PI)
|
||||
#define C_PI (3.14159265358979323846f) // (3.141592654f)
|
||||
#define TO_RAD (C_PI/180)
|
||||
#define TO_DEG (180/C_PI)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -984,7 +988,6 @@ API void randset(uint64_t state);
|
|||
API uint64_t rand64(void);
|
||||
API double randf(void); // [0, 1) interval
|
||||
API int randi(int mini, int maxi); // [mini, maxi) interval
|
||||
//API double rng(void); // [0..1) Lehmer RNG "minimal standard"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -1050,10 +1053,10 @@ enum EASE_FLAGS {
|
|||
EASE_OUT = 0,
|
||||
};
|
||||
|
||||
API float ease(float t01, unsigned mode); // 0=linear,1=out_sine...31=inout_perlin
|
||||
|
||||
API float ease_ping_pong(float t, float(*fn1)(float), float(*fn2)(float));
|
||||
API float ease_pong_ping(float t, float(*fn1)(float), float(*fn2)(float));
|
||||
API float ease(float t01, unsigned fn); // / 0-to-1
|
||||
API float ease_pong(float t01, unsigned fn); // \ 1-to-0
|
||||
API float ease_ping_pong(float t, unsigned fn1, unsigned fn2); // /\ 0-to-1-to-0
|
||||
API float ease_pong_ping(float t, unsigned fn1, unsigned fn2); // \/ 1-to-0-to-1
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -2042,6 +2045,64 @@ bool id_valid(uintptr_t id);
|
|||
#line 0
|
||||
|
||||
#line 1 "engine/split/v4k_pack.h"
|
||||
// -----------------------------------------------------------------------------
|
||||
// semantic versioning in a single byte (octal)
|
||||
// - rlyeh, public domain.
|
||||
//
|
||||
// - single octal byte that represents semantic versioning (major.minor.patch).
|
||||
// - allowed range [0000..0377] ( <-> [0..255] decimal )
|
||||
// - comparison checks only major.minor tuple as per convention.
|
||||
|
||||
API int semver( int major, int minor, int patch );
|
||||
API int semvercmp( int v1, int v2 );
|
||||
|
||||
#define SEMVER(major,minor,patch) (0100 * (major) + 010 * (minor) + (patch))
|
||||
#define SEMVERCMP(v1,v2) (((v1) & 0110) - ((v2) & 0110))
|
||||
#define SEMVERFMT "%03o"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// storage types. refer to vec2i/3i, vec2/3/4 if you plan to do math operations
|
||||
|
||||
typedef struct byte2 { uint8_t x,y; } byte2;
|
||||
typedef struct byte3 { uint8_t x,y,z; } byte3;
|
||||
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
|
||||
|
||||
typedef struct int2 { int x,y; } int2;
|
||||
typedef struct int3 { int x,y,z; } int3;
|
||||
typedef struct int4 { int x,y,z,w; } int4;
|
||||
|
||||
typedef struct uint2 { unsigned int x,y; } uint2;
|
||||
typedef struct uint3 { unsigned int x,y,z; } uint3;
|
||||
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
|
||||
|
||||
typedef struct float2 { float x,y; } float2;
|
||||
typedef struct float3 { float x,y,z; } float3;
|
||||
typedef struct float4 { float x,y,z,w; } float4;
|
||||
|
||||
typedef struct double2 { double x,y; } double2;
|
||||
typedef struct double3 { double x,y,z; } double3;
|
||||
typedef struct double4 { double x,y,z,w; } double4;
|
||||
|
||||
#define byte2(x,y) M_CAST(byte2, (uint8_t)(x), (uint8_t)(y) )
|
||||
#define byte3(x,y,z) M_CAST(byte3, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z) )
|
||||
#define byte4(x,y,z,w) M_CAST(byte4, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z), (uint8_t)(w) )
|
||||
|
||||
#define int2(x,y) M_CAST(int2, (int)(x), (int)(y) )
|
||||
#define int3(x,y,z) M_CAST(int3, (int)(x), (int)(y), (int)(z) )
|
||||
#define int4(x,y,z,w) M_CAST(int4, (int)(x), (int)(y), (int)(z), (int)(w) )
|
||||
|
||||
#define uint2(x,y) M_CAST(uint2, (unsigned)(x), (unsigned)(y) )
|
||||
#define uint3(x,y,z) M_CAST(uint3, (unsigned)(x), (unsigned)(y), (unsigned)(z) )
|
||||
#define uint4(x,y,z,w) M_CAST(uint4, (unsigned)(x), (unsigned)(y), (unsigned)(z), (unsigned)(w) )
|
||||
|
||||
#define float2(x,y) M_CAST(float2, (float)(x), (float)(y) )
|
||||
#define float3(x,y,z) M_CAST(float3, (float)(x), (float)(y), (float)(z) )
|
||||
#define float4(x,y,z,w) M_CAST(float4, (float)(x), (float)(y), (float)(z), (float)(w) )
|
||||
|
||||
#define double2(x,y) M_CAST(double2, (double)(x), (double)(y) )
|
||||
#define double3(x,y,z) M_CAST(double3, (double)(x), (double)(y), (double)(z) )
|
||||
#define double4(x,y,z,w) M_CAST(double4, (double)(x), (double)(y), (double)(z), (double)(w) )
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// compile-time fourcc, eightcc
|
||||
|
||||
|
@ -2070,16 +2131,25 @@ enum {
|
|||
#define cc7(a,b,c,d,e,f,g) cc8(,a,b,c,d,e,f,g)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// float conversion (text)
|
||||
// text conversions
|
||||
|
||||
API char* ftoa1(float v);
|
||||
API char* ftoa2(vec2 v);
|
||||
API char* ftoa3(vec3 v);
|
||||
API char* ftoa4(vec4 v);
|
||||
|
||||
API float atof1(const char *s);
|
||||
API vec2 atof2(const char *s);
|
||||
API vec3 atof3(const char *s);
|
||||
API vec4 atof4(const char *s);
|
||||
|
||||
API char* ftoa(float f);
|
||||
API char* ftoa2(vec2 v);
|
||||
API char* ftoa3(vec3 v);
|
||||
API char* ftoa4(vec4 v);
|
||||
API char* itoa1(int v);
|
||||
API char* itoa2(vec2i v);
|
||||
API char* itoa3(vec3i v);
|
||||
|
||||
API int atoi1(const char *s);
|
||||
API vec2i atoi2(const char *s);
|
||||
API vec3i atoi3(const char *s);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// endianness
|
||||
|
@ -2574,63 +2644,312 @@ API int64_t client_join(const char *ip, int port);
|
|||
#line 0
|
||||
|
||||
#line 1 "engine/split/v4k_obj.h"
|
||||
// -----------------------------------------------------------------------------
|
||||
// semantic versioning in a single byte (octal)
|
||||
// C objects framework
|
||||
// - rlyeh, public domain.
|
||||
//
|
||||
// - single octal byte that represents semantic versioning (major.minor.patch).
|
||||
// - allowed range [0000..0377] ( <-> [0..255] decimal )
|
||||
// - comparison checks only major.minor tuple as per convention.
|
||||
// ## object limitations
|
||||
// - 8-byte overhead per object
|
||||
// - XX-byte overhead per object-entity
|
||||
// - 32 components max per object-entity
|
||||
// - 256 classes max per game
|
||||
// - 256 references max per object
|
||||
// - 1024K bytes max per object
|
||||
// - 8 generations + 64K IDs per running instance (19-bit IDs)
|
||||
// - support for pragma pack(1) structs not enabled by default.
|
||||
|
||||
API int semver( int major, int minor, int patch );
|
||||
API int semvercmp( int v1, int v2 );
|
||||
/* /!\ if you plan to use pragma pack(1) on any struct, you need #define OBJ_MIN_PRAGMAPACK_BITS 0 at the expense of max class size /!\ */
|
||||
#ifndef OBJ_MIN_PRAGMAPACK_BITS
|
||||
//#define OBJ_MIN_PRAGMAPACK_BITS 3 // allows pragma packs >= 8. objsizew becomes 7<<3, so 1024 bytes max per class (default)
|
||||
#define OBJ_MIN_PRAGMAPACK_BITS 1 // allows pragma packs >= 2. objsizew becomes 7<<1, so 256 bytes max per class
|
||||
//#define OBJ_MIN_PRAGMAPACK_BITS 0 // allows pragma packs >= 1. objsizew becomes 7<<0, so 128 bytes max per class
|
||||
#endif
|
||||
|
||||
#define SEMVER(major,minor,patch) (0100 * (major) + 010 * (minor) + (patch))
|
||||
#define SEMVERCMP(v1,v2) (((v1) & 0110) - ((v2) & 0110))
|
||||
#define SEMVERFMT "%03o"
|
||||
#define OBJHEADER \
|
||||
union { \
|
||||
uintptr_t objheader; \
|
||||
struct { \
|
||||
uintptr_t objtype:8; \
|
||||
uintptr_t objheap:1; \
|
||||
uintptr_t objsizew:7; \
|
||||
uintptr_t objrefs:8; \
|
||||
uintptr_t objcomps:1; /* << can be removed? check payload ptr instead? */ \
|
||||
uintptr_t objnameid:16; \
|
||||
uintptr_t objid:ID_INDEX_BITS+ID_COUNT_BITS; /*16+3*/ \
|
||||
uintptr_t objunused:64-8-7-1-1-8-16-ID_INDEX_BITS-ID_COUNT_BITS; /*4*/ \
|
||||
}; \
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// storage types. refer to vec2i/3i, vec2/3/4 if you plan to do math operations
|
||||
#define OBJ \
|
||||
struct { OBJHEADER };
|
||||
|
||||
typedef struct byte2 { uint8_t x,y; } byte2;
|
||||
typedef struct byte3 { uint8_t x,y,z; } byte3;
|
||||
typedef struct byte4 { uint8_t x,y,z,w; } byte4;
|
||||
// ----------------------------------------------------------------------------
|
||||
// syntax sugars
|
||||
|
||||
typedef struct int2 { int x,y; } int2;
|
||||
typedef struct int3 { int x,y,z; } int3;
|
||||
typedef struct int4 { int x,y,z,w; } int4;
|
||||
#ifdef OBJTYPE
|
||||
#undef OBJTYPE
|
||||
#endif
|
||||
|
||||
typedef struct uint2 { unsigned int x,y; } uint2;
|
||||
typedef struct uint3 { unsigned int x,y,z; } uint3;
|
||||
typedef struct uint4 { unsigned int x,y,z,w; } uint4;
|
||||
#define OBJTYPE(T) \
|
||||
OBJTYPE_##T
|
||||
|
||||
typedef struct float2 { float x,y; } float2;
|
||||
typedef struct float3 { float x,y,z; } float3;
|
||||
typedef struct float4 { float x,y,z,w; } float4;
|
||||
#define OBJTYPEDEF(NAME,N) \
|
||||
enum { OBJTYPE(NAME) = N }; \
|
||||
STATIC_ASSERT( N <= 255 ); \
|
||||
STATIC_ASSERT( (sizeof(NAME) & ((1<<OBJ_MIN_PRAGMAPACK_BITS)-1)) == 0 );
|
||||
|
||||
typedef struct double2 { double x,y; } double2;
|
||||
typedef struct double3 { double x,y,z; } double3;
|
||||
typedef struct double4 { double x,y,z,w; } double4;
|
||||
// ----------------------------------------------------------------------------
|
||||
// objects
|
||||
|
||||
#define byte2(x,y) M_CAST(byte2, (uint8_t)(x), (uint8_t)(y) )
|
||||
#define byte3(x,y,z) M_CAST(byte3, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z) )
|
||||
#define byte4(x,y,z,w) M_CAST(byte4, (uint8_t)(x), (uint8_t)(y), (uint8_t)(z), (uint8_t)(w) )
|
||||
#define TYPEDEF_STRUCT(NAME,N,...) \
|
||||
typedef struct NAME { OBJ \
|
||||
__VA_ARGS__ \
|
||||
char payload[0]; \
|
||||
} NAME; OBJTYPEDEF(NAME,N);
|
||||
|
||||
#define int2(x,y) M_CAST(int2, (int)(x), (int)(y) )
|
||||
#define int3(x,y,z) M_CAST(int3, (int)(x), (int)(y), (int)(z) )
|
||||
#define int4(x,y,z,w) M_CAST(int4, (int)(x), (int)(y), (int)(z), (int)(w) )
|
||||
// TYPEDEF_STRUCT(obj,0);
|
||||
typedef struct obj { OBJ } obj;
|
||||
|
||||
#define uint2(x,y) M_CAST(uint2, (unsigned)(x), (unsigned)(y) )
|
||||
#define uint3(x,y,z) M_CAST(uint3, (unsigned)(x), (unsigned)(y), (unsigned)(z) )
|
||||
#define uint4(x,y,z,w) M_CAST(uint4, (unsigned)(x), (unsigned)(y), (unsigned)(z), (unsigned)(w) )
|
||||
// ----------------------------------------------------------------------------
|
||||
// entities
|
||||
|
||||
#define float2(x,y) M_CAST(float2, (float)(x), (float)(y) )
|
||||
#define float3(x,y,z) M_CAST(float3, (float)(x), (float)(y), (float)(z) )
|
||||
#define float4(x,y,z,w) M_CAST(float4, (float)(x), (float)(y), (float)(z), (float)(w) )
|
||||
#define OBJCOMPONENTS_MAX 32
|
||||
#define OBJCOMPONENTS_ALL_ENABLED 0xAAAAAAAAAAAAAAAAULL
|
||||
#define OBJCOMPONENTS_ALL_FLAGGED 0x5555555555555555ULL
|
||||
#define COMPONENTS_ONLY(x) ((x) & ~OBJCOMPONENTS_ALL_FLAGGED)
|
||||
|
||||
#define ENTITY \
|
||||
struct { OBJHEADER union { struct { uintptr_t objenabled:OBJCOMPONENTS_MAX, objflagged:OBJCOMPONENTS_MAX; }; uintptr_t cflags; }; void *c[OBJCOMPONENTS_MAX]; };
|
||||
|
||||
#define TYPEDEF_ENTITY(NAME,N,...) \
|
||||
typedef struct NAME { ENTITY \
|
||||
__VA_ARGS__ \
|
||||
char payload[0]; \
|
||||
} NAME; OBJTYPEDEF(NAME,N);
|
||||
|
||||
// OBJTYPEDEF(entity,1)
|
||||
typedef struct entity { ENTITY } entity;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// heap/stack ctor/dtor
|
||||
|
||||
static __thread obj *objtmp;
|
||||
#define OBJ_CTOR_HDR(PTR,HEAP,OBJ_NAMEID,SIZEOF_OBJ,OBJ_TYPE) ( \
|
||||
(PTR)->objheader = HEAP ? id_make(PTR) : 0, /*should assign to .objid instead. however, id_make() returns shifted bits already*/ \
|
||||
(PTR)->objnameid = (OBJ_NAMEID), \
|
||||
(PTR)->objtype = (OBJ_TYPE), \
|
||||
(PTR)->objheap = (HEAP), \
|
||||
(PTR)->objsizew = (SIZEOF_OBJ>>OBJ_MIN_PRAGMAPACK_BITS))
|
||||
#define OBJ_CTOR_PTR(PTR,HEAP,OBJ_NAMEID,SIZEOF_OBJ,OBJ_TYPE) ( \
|
||||
OBJ_CTOR_HDR(PTR,HEAP,OBJ_NAMEID,SIZEOF_OBJ,OBJ_TYPE), \
|
||||
obj_ctor(PTR))
|
||||
#define OBJ_CTOR(TYPE, NAME, HEAP, PAYLOAD_SIZE, ...) (TYPE*)( \
|
||||
objtmp = (HEAP ? MALLOC(sizeof(TYPE)+(PAYLOAD_SIZE)) : ALLOCA(sizeof(TYPE)+(PAYLOAD_SIZE))), \
|
||||
*(TYPE*)objtmp = ((TYPE){ {0}, __VA_ARGS__}), \
|
||||
((PAYLOAD_SIZE) ? memset((char*)objtmp + sizeof(TYPE), 0, (PAYLOAD_SIZE)) : objtmp), \
|
||||
( OBJTYPES[ OBJTYPE(TYPE) ] = #TYPE ), \
|
||||
OBJ_CTOR_PTR(objtmp, HEAP,intern(NAME),sizeof(TYPE),OBJTYPE(TYPE)), \
|
||||
ifdef(debug, (obj_printf)(objtmp, va("%s", callstack(+16))), 0), \
|
||||
objtmp)
|
||||
|
||||
#define obj(TYPE, ...) obj_ext(TYPE, #TYPE, __VA_ARGS__)
|
||||
#define obj_ext(TYPE, NAME, ...) *OBJ_CTOR(TYPE, NAME, 0, sizeof(array(obj*)), __VA_ARGS__)
|
||||
|
||||
#define obj_new(TYPE, ...) obj_new_ext(TYPE, #TYPE, __VA_ARGS__)
|
||||
#define obj_new_ext(TYPE, NAME, ...) OBJ_CTOR(TYPE, NAME, 1, sizeof(array(obj*)), __VA_ARGS__)
|
||||
|
||||
void* obj_malloc(unsigned sz);
|
||||
void* obj_free(void *o);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// obj generics. can be extended.
|
||||
|
||||
#define obj_ctor(o,...) obj_method(ctor, o, ##__VA_ARGS__)
|
||||
#define obj_dtor(o,...) obj_method(dtor, o, ##__VA_ARGS__)
|
||||
|
||||
#define obj_save(o,...) obj_method(save, o, ##__VA_ARGS__)
|
||||
#define obj_load(o,...) obj_method(load, o, ##__VA_ARGS__)
|
||||
|
||||
#define obj_test(o,...) obj_method(test, o, ##__VA_ARGS__)
|
||||
|
||||
#define obj_init(o,...) obj_method(init, o, ##__VA_ARGS__)
|
||||
#define obj_quit(o,...) obj_method(quit, o, ##__VA_ARGS__)
|
||||
#define obj_tick(o,...) obj_method(tick, o, ##__VA_ARGS__)
|
||||
#define obj_draw(o,...) obj_method(draw, o, ##__VA_ARGS__)
|
||||
|
||||
#define obj_lerp(o,...) obj_method(lerp, o, ##__VA_ARGS__)
|
||||
#define obj_edit(o,...) obj_method(edit, o, ##__VA_ARGS__)
|
||||
|
||||
// --- syntax sugars
|
||||
|
||||
#define obj_extend(T,func) (obj_##func[OBJTYPE(T)] = (void*)T##_##func)
|
||||
#define obj_method(method,o,...) (obj_##method[((obj*)(o))->objtype](o,##__VA_ARGS__)) // (obj_##method[((obj*)(o))->objtype]((o), ##__VA_ARGS__))
|
||||
|
||||
#define obj_vtable(func,RC,...) RC macro(obj_##func)(){ __VA_ARGS__ }; RC (*obj_##func[256])() = { REPEAT256(macro(obj_##func)) };
|
||||
#define obj_vtable_null(func,RC) RC (*obj_##func[256])() = { 0 }; // null virtual table. will crash unless obj_extend'ed
|
||||
|
||||
#define REPEAT16(f) f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f
|
||||
#define REPEAT64(f) REPEAT16(f),REPEAT16(f),REPEAT16(f),REPEAT16(f)
|
||||
#define REPEAT256(f) REPEAT64(f),REPEAT64(f),REPEAT64(f),REPEAT64(f)
|
||||
|
||||
// --- declare vtables
|
||||
|
||||
API extern void (*obj_ctor[256])(); ///-
|
||||
API extern void (*obj_dtor[256])(); ///-
|
||||
|
||||
API extern char* (*obj_save[256])(); ///-
|
||||
API extern bool (*obj_load[256])(); ///-
|
||||
API extern int (*obj_test[256])(); ///-
|
||||
|
||||
API extern int (*obj_init[256])(); ///-
|
||||
API extern int (*obj_quit[256])(); ///-
|
||||
API extern int (*obj_tick[256])(); ///-
|
||||
API extern int (*obj_draw[256])(); ///-
|
||||
|
||||
API extern int (*obj_lerp[256])(); ///-
|
||||
API extern int (*obj_edit[256])(); ///-
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// core
|
||||
|
||||
API uintptr_t obj_header(const void *o);
|
||||
|
||||
API uintptr_t obj_id(const void *o);
|
||||
API const char* obj_name(const void *o);
|
||||
|
||||
API unsigned obj_typeid(const void *o);
|
||||
API const char* obj_type(const void *o);
|
||||
|
||||
API int obj_sizeof(const void *o);
|
||||
API int obj_size(const void *o); // size of all members together in struct. may include padding bytes.
|
||||
|
||||
API char* obj_data(void *o); // pointer to the first member in struct
|
||||
API const char* obj_datac(const void *o); // const pointer to the first struct member
|
||||
|
||||
API void* obj_payload(const void *o); // pointer right after last member in struct
|
||||
API void* obj_zero(void *o); // reset all object members
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// refcounting
|
||||
|
||||
API void* obj_ref(void *oo);
|
||||
API void* obj_unref(void *oo);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// scene tree
|
||||
|
||||
// non-recursive
|
||||
#define each_objchild(p,t,o) \
|
||||
(array(obj*)* children = obj_children(p); children; children = 0) \
|
||||
for(int _i = 1, _end = array_count(*children); _i < _end; ++_i) \
|
||||
for(t *o = (t *)((*children)[_i]); o && 0[*children]; o = 0)
|
||||
|
||||
API obj* obj_detach(void *c);
|
||||
API obj* obj_attach(void *o, void *c);
|
||||
|
||||
API obj* obj_root(const void *o);
|
||||
API obj* obj_parent(const void *o);
|
||||
API array(obj*)*obj_children(const void *o);
|
||||
API array(obj*)*obj_siblings(const void *o);
|
||||
|
||||
API int obj_dumptree(const void *o);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// metadata
|
||||
|
||||
API const char* obj_metaset(const void *o, const char *key, const char *value);
|
||||
API const char* obj_metaget(const void *o, const char *key);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// stl
|
||||
|
||||
API void* obj_swap(void *dst, void *src);
|
||||
API void* obj_copy_fast(void *dst, const void *src);
|
||||
API void* obj_copy(void *dst, const void *src);
|
||||
|
||||
API int obj_comp_fast(const void *a, const void *b);
|
||||
API int obj_comp(const void *a, const void *b);
|
||||
API int obj_lesser(const void *a, const void *b);
|
||||
API int obj_greater(const void *a, const void *b);
|
||||
API int obj_equal(const void *a, const void *b);
|
||||
|
||||
API uint64_t obj_hash(const void *o);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// debug
|
||||
|
||||
API bool obj_hexdump(const void *oo);
|
||||
API int obj_print(const void *o);
|
||||
|
||||
API int obj_printf(const void *o, const char *text);
|
||||
API int obj_console(const void *o); // obj_output() ?
|
||||
|
||||
#define obj_printf(o, ...) obj_printf(o, va(__VA_ARGS__))
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// serialization
|
||||
|
||||
API char* obj_saveini(const void *o);
|
||||
API obj* obj_mergeini(void *o, const char *ini);
|
||||
API obj* obj_loadini(void *o, const char *ini);
|
||||
|
||||
API char* obj_savejson(const void *o);
|
||||
API obj* obj_mergejson(void *o, const char *json);
|
||||
API obj* obj_loadjson(void *o, const char *json);
|
||||
|
||||
API char* obj_savebin(const void *o);
|
||||
API obj* obj_mergebin(void *o, const char *sav);
|
||||
API obj* obj_loadbin(void *o, const char *sav);
|
||||
|
||||
API char* obj_savempack(const void *o); // @todo
|
||||
API obj* obj_mergempack(void *o, const char *sav); // @todo
|
||||
API obj* obj_loadmpack(void *o, const char *sav); // @todo
|
||||
|
||||
API int obj_push(const void *o);
|
||||
API int obj_pop(void *o);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// components
|
||||
|
||||
API bool obj_addcomponent(void *object, unsigned c, void *ptr);
|
||||
API bool obj_hascomponent(void *object, unsigned c);
|
||||
API void* obj_getcomponent(void *object, unsigned c);
|
||||
API bool obj_delcomponent(void *object, unsigned c);
|
||||
API bool obj_usecomponent(void *object, unsigned c);
|
||||
API bool obj_offcomponent(void *object, unsigned c);
|
||||
|
||||
API char* entity_save(entity *self);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// reflection
|
||||
|
||||
#define each_objmember(oo,TYPE,NAME,PTR) \
|
||||
(array(reflect_t) *found_ = members_find(quark(((obj*)oo)->objnameid)); found_; found_ = 0) \
|
||||
for(int it_ = 0, end_ = array_count(*found_); it_ != end_; ++it_ ) \
|
||||
for(reflect_t *R = &(*found_)[it_]; R; R = 0 ) \
|
||||
for(const char *NAME = R->name, *TYPE = R->type; NAME || TYPE; ) \
|
||||
for(void *PTR = ((char*)oo) + R->sz ; NAME || TYPE ; NAME = TYPE = 0 )
|
||||
|
||||
API void* obj_clone(const void *src);
|
||||
API void* obj_merge(void *dst, const void *src); // @testme
|
||||
API void* obj_mutate(void **dst, const void *src);
|
||||
API void* obj_make(const char *str);
|
||||
|
||||
// built-ins
|
||||
|
||||
typedef enum OBJTYPE_BUILTINS {
|
||||
OBJTYPE_obj = 0,
|
||||
OBJTYPE_entity = 1,
|
||||
OBJTYPE_vec2 = 2,
|
||||
OBJTYPE_vec3 = 3,
|
||||
OBJTYPE_vec4 = 4,
|
||||
OBJTYPE_quat = 5,
|
||||
OBJTYPE_mat33 = 6,
|
||||
OBJTYPE_mat34 = 7,
|
||||
OBJTYPE_mat44 = 8,
|
||||
OBJTYPE_vec2i = 9,
|
||||
OBJTYPE_vec3i = 10,
|
||||
} OBJTYPE_BUILTINS;
|
||||
|
||||
#define double2(x,y) M_CAST(double2, (double)(x), (double)(y) )
|
||||
#define double3(x,y,z) M_CAST(double3, (double)(x), (double)(y), (double)(z) )
|
||||
#define double4(x,y,z,w) M_CAST(double4, (double)(x), (double)(y), (double)(z), (double)(w) )
|
||||
#line 0
|
||||
|
||||
#line 1 "engine/split/v4k_profile.h"
|
||||
|
@ -2694,15 +3013,15 @@ typedef struct reflect_t {
|
|||
// inscribe api
|
||||
|
||||
#define ENUM(V, .../*value_annotations*/) \
|
||||
enum_inscribe(#V,intern(#V),V, "" __VA_ARGS__/*value_annotations*/)
|
||||
enum_inscribe(#V,V, "" __VA_ARGS__/*value_annotations*/)
|
||||
|
||||
#define FUNCTION(F, .../*function_annotations*/) \
|
||||
function_inscribe(#F,intern(#F),(void*)F, "" __VA_ARGS__/*function_annotations*/)
|
||||
function_inscribe(#F,(void*)F, "" __VA_ARGS__/*function_annotations*/)
|
||||
|
||||
#define STRUCT(T, type, member, .../*member_annotations*/) \
|
||||
struct_inscribe(#T,intern(#T),sizeof(T),OBJTYPE(T),NULL), \
|
||||
type_inscribe(#type,intern(#type),sizeof(((T){0}).member),"" __VA_ARGS__/*member_annotations*/), \
|
||||
member_inscribe(intern(#T), #member,intern(#member),(uintptr_t)&((T*)0)->member, "" __VA_ARGS__/*member_annotations*/, #type, sizeof(((T){0}).member) )
|
||||
struct_inscribe(#T,sizeof(T),OBJTYPE(T),NULL), \
|
||||
type_inscribe(#type,sizeof(((T){0}).member),"" __VA_ARGS__/*member_annotations*/), \
|
||||
member_inscribe(#T, #member,(uintptr_t)&((T*)0)->member, "" __VA_ARGS__/*member_annotations*/, #type, sizeof(((T){0}).member) )
|
||||
|
||||
// find api
|
||||
|
||||
|
@ -2711,22 +3030,22 @@ API void * function_find(const char *F);
|
|||
|
||||
API reflect_t member_find(const char *T, const char *M); /// find specific member
|
||||
API void * member_findptr(void *obj, const char *T, const char *M); // @deprecate
|
||||
API array(reflect_t) members_find(const char *T);
|
||||
API array(reflect_t)* members_find(const char *T);
|
||||
|
||||
// iterate members in a struct
|
||||
|
||||
#define each_member(T,R) \
|
||||
(array(reflect_t)*found_ = map_find(members, intern(T)); found_; found_ = 0) \
|
||||
(array(reflect_t) *found_ = members_find(T); found_; found_ = 0) \
|
||||
for(int it_ = 0, end_ = array_count(*found_); it_ != end_; ++it_ ) \
|
||||
for(reflect_t *R = (*found_)+it_; R; R = 0 )
|
||||
for(reflect_t *R = &(*found_)[it_]; R; R = 0 )
|
||||
|
||||
// private api, still exposed
|
||||
|
||||
API void type_inscribe(const char *TY,unsigned TYid,unsigned TYsz,const char *infos);
|
||||
API void enum_inscribe(const char *E,unsigned Eid,unsigned Eval,const char *infos);
|
||||
API void struct_inscribe(const char *T,unsigned Tid,unsigned Tsz,unsigned OBJTYPEid, const char *infos);
|
||||
API void member_inscribe(unsigned Tid, const char *M,unsigned Mid,unsigned Msz, const char *infos, const char *type, unsigned bytes);
|
||||
API void function_inscribe(const char *F,unsigned Fid,void *func,const char *infos);
|
||||
API void type_inscribe(const char *TY,unsigned TYsz,const char *infos);
|
||||
API void enum_inscribe(const char *E,unsigned Eval,const char *infos);
|
||||
API void struct_inscribe(const char *T,unsigned Tsz,unsigned OBJTYPEid, const char *infos);
|
||||
API void member_inscribe(const char *T, const char *M,unsigned Msz, const char *infos, const char *type, unsigned bytes);
|
||||
API void function_inscribe(const char *F,void *func,const char *infos);
|
||||
|
||||
API void reflect_print(const char *symbol);
|
||||
API void reflect_dump(const char *mask);
|
||||
|
@ -2751,7 +3070,7 @@ API unsigned rgba( uint8_t r, uint8_t g, uint8_t b, uint8_t a );
|
|||
API unsigned bgra( uint8_t b, uint8_t g, uint8_t r, uint8_t a );
|
||||
API unsigned rgbaf( float r, float g, float b, float a );
|
||||
API unsigned bgraf( float b, float g, float r, float a );
|
||||
API float alpha( unsigned rgba );
|
||||
API unsigned alpha( unsigned rgba );
|
||||
|
||||
#define RGBX(rgb,x) ( ((rgb)&0xFFFFFF) | (((unsigned)(x))<<24) )
|
||||
#define RGB3(r,g,b) ( (255<<24) | ((r)<<16) | ((g)<<8) | (b) )
|
||||
|
@ -3737,16 +4056,16 @@ API array(uint32_t) string32( const char *utf8 ); /// convert from utf8 to utf32
|
|||
// ## string interning (quarks)
|
||||
// - rlyeh, public domain.
|
||||
|
||||
unsigned intern( const char *string );
|
||||
const char *quark( unsigned key );
|
||||
API unsigned intern( const char *string );
|
||||
API const char *quark( unsigned key );
|
||||
|
||||
typedef struct quarks_db {
|
||||
array(char) blob;
|
||||
array(vec2i) entries;
|
||||
} quarks_db;
|
||||
|
||||
unsigned quark_intern( quarks_db*, const char *string );
|
||||
const char *quark_string( quarks_db*, unsigned key );
|
||||
API unsigned quark_intern( quarks_db*, const char *string );
|
||||
API const char *quark_string( quarks_db*, unsigned key );
|
||||
#line 0
|
||||
|
||||
#line 1 "engine/split/v4k_time.h"
|
||||
|
@ -3776,7 +4095,7 @@ API void timer_destroy(unsigned timer_handle);
|
|||
//
|
||||
// also similar to a mongo object id, 12 bytes as follows:
|
||||
// - 4-byte timestamp (ss). epoch: Tuesday, 12 September 2023 6:06:56
|
||||
// - 2-byte (machine or app hash)
|
||||
// - 2-byte (machine, hash or app id)
|
||||
// - 2-byte (thread-id)
|
||||
// - 4-byte (rand counter, that gets increased at every id creation)
|
||||
|
||||
|
@ -3848,7 +4167,7 @@ API void die(const char *message);
|
|||
API void alert(const char *message);
|
||||
API void hexdump( const void *ptr, unsigned len );
|
||||
API void hexdumpf( FILE *fp, const void *ptr, unsigned len, int width );
|
||||
API void breakpoint(const char *optional_reason);
|
||||
API void breakpoint();
|
||||
API bool has_debugger();
|
||||
|
||||
API void trap_install(void);
|
||||
|
|
Loading…
Reference in New Issue