lbc - helper macro/api to embed luajit code into C
#include "lbc.h"
typedef struct struct lbc_tag {
lua_t* L;
char* emsg;
int rc;
char glname[64];
int lpflg;
int used;
} lbc_t;
typedef struct lbc_rttag {
int inner_flg;
int tp;
int i;
double d;
const char* s;
int sz;
} lbc_rt;
//runner
lbc_t*
lbc_new(void);
void lbc_free(lbc_t*
obj);
int lbc_reqbcM(lbc_t*
obj,
...
);
int lbc_runbcM(lbc_t*
obj,
int
ac,
char**
av,
FILE_IDENT );
int lbc_runstr(lbc_t*
obj,
int
ac,
char**
av,
const char*
str) ;
int lbc_prunbcM(lbc_t*
obj,
int
ac,
char**
av,
FILE_IDENT );
int lbc_prunstr(lbc_t*
obj,
int
ac,
char**
av,
const char*
str) ;
//result-reader
lbc_rt lbc_tbval(lbc_t*
obj,
...
);
int lbc_tbvalsv(int nidx,
lbc_t*
obj, ...
);
int lbc_delsv(lbc_t*
obj,
int
idx );
int lbc_cleansv(lbc_t*
obj);
int lbc_directf(int nidx,
lbc_t*
obj, ...
);
int lbc_pdirectf(int nidx,
lbc_t*
obj, ...
);
#define LBC_C2P //see NOTES. for directf/pdirectf
#define LBC_P2C
#define LBC_P2S
//result-support
lbc_pairs(k, v, obj, ...){...} //macro
lbc_ipairs(i, v, obj, ...){...}
int lbc_keysv(int nidx,
lbc_t*
obj );
int lbc_valsv(int nidx,
lbc_t*
obj );
//lhh, use luatb as hash instead of c99 hsearch()
typedef struct luahash_tag{
lua_t* L;
char* emsg;
int rc;
char glname[64];
int lpflg;
int cnt;
} lhh_t;
typedef lbc_rt lhh_rt;
lhh_t*
lhh_new(void);
void lhh_free(lhh_t*
obj);
int lhh_set(lhh_t*
obj,
key,
val, ...
);
int lhh_unset(lhh_t*
obj,
key);
lhh_rt lhh_get(lhh_t*
obj,
key);
// use `double` args instead of `int`
int lhh_setd(lhh_t*
obj,
key,
val, ...
);
int lhh_unsetd(lhh_t*
obj,
key);
lhh_rt lhh_getd(lhh_t*
obj,
key) ;
#include "lbc.h"
int main(int argc, char** argv) {
lbc_t* obj = lbc_new();
int rc = lbc_runstr(obj, argc, argv, "print('from_lua');return 123");
lbc_rt val = lbc_tbval(obj, 0, 1);
printf("%d\n", val.i); //>> 123
lbc_free(obj);
return 0;
}
//~$ gcc src.c liblbc.a -lm -ldl
lbc assists embed luajit code to C. basic API is:
- lbc_new() makes luajit vm
- lbc_reqbcM() imports dependent lj bytecode file as require()
- lbc_runbcM(), lbc_runstr() execute byte/str code
- lbc_tbval() gets rtn/tb val with C type, int, char* etc
- lbc_free() closes luajit vm and release memory
additional API is:
- lbc_directf() executes luajit code directly as lbc_runstr()
only available after runbcM()/runstr()
- prunbcM(), prunstr(), pdirectf() catches assert() and rtn emsg
- lbc_tbvalsv(), delsv(), cleansv() treats rtn/tb val
- lbc_pairs(), ipairs(), keysv(), valsv() handle luatb from C.
mimic the syntax of lua.
lhh is hash api using luatb. lhh is independet from lbc. lhh can uses
lbc_pairs() / lbc_ipairs() macro. see samples.
- lhh_new() creates hashtable. setup luavm and luatb internally.
- lhh_set() sets key/val to hash.
- lhh_unset() removes key/val from hash.
- lhh_get() gets val from hash.
- lhh_free() closes hash(luavm) and releases memory.
setd(),unsetd(),getd() uses double type instead of integer.
--mod.lua
local M={}
function M.myf() print("from_mod"); return 10; end
return M
--run.lua
local m = require("mod")
m.myf()
return -3, "gw"
-- ~$ luajit run.lua #>> (test_run) from_mod
-- ~$ luajit -b mod.lua mod.h #>> prepare bytecode mod.h
-- ~$ luajit -b run.lua run.h #>> run.h
//--src.c
#include "lbc.h"
int main(int argc, char** argv) {
lbc_t* obj = lbc_new();
#include "mod.h"
lbc_reqbcM(obj, mod); //macro: mod >> "mod.h" strlen(mod_XX);
#include "run.h"
int n = lbc_runbcM(obj, argc, argv, run); //n=len{-3, "gw"}=2
// set argv to _G.arg, works as ~$ luajit run.lua ag1 ag2 ...
lbc_rt val = lbc_tbval(obj, 0, 2); // val=obj[0][2]
printf("%s\n", val.s); // val.tp=='s'tring, val.s== "gw"
lbc_free(obj);
return 0;
}
//~$ gcc src.c -static liblbc.a -lm -ldl
//(or ~$ gcc src.c -lluajit5.1 -lm -ldl liblbc.a)
//..liblbc.a holds whole libluajit5.1.a files.
//--------src2.c, detail sample code
#include "lbc.h"
int main(int ac, char** av) {
lbc_t* obj = lbc_new();
#include "mod1.h"
#include "mod2.h"
#include "mod3.h"
#include "mod4.h"
lbc_reqbcM(obj, mod1, mod2, mod3); // support at least 9 args
lbc_reqbcM(obj, mod4); // allow req many times
int n = lbc_runstr(obj, ac, av, "return require('mod2').myf()");
//call only once lbc_runbcM() or lbc_runstr()
lbc_rt val=lbc_tbval(obj,0,1); //obj[0]={r1,r2}, obj[0][1]=r1 etc
//luartns are saved to [0], return 1,2,{10,20} >> obj[0]={1,2,{10,20}}
//- int val.tp='s': (n)um(s)tr(t)b(b)ool(N)il(f)c(T)hread(u)data(l)udata
//- tp='s' >> sz=bytesz, tp='t' >> sz==#tb (tb[1]-tb[n], cnt only seq)
//- tp: val.i/val.d/val.s/val.sz == int/dbl/const char* /int
//- test yourself if needs tp check: val.d-val.i==0 (...maybe int)
//- true/false,nil is converted to int val.i 1/0
//- val cant hold non-C data, table/func etc. val.tp check only.
// allows int/str key, tbval(obj,0,"k",&p,n)==obj[0]["k"]["str"][3]
//- use as str if ag starts with "/&. char* p="str", char** p=&str
//- strkey cant holds '\0'. not "/& args are treated as int
//- dbl cant use as key, lbc_tbval(obj, 3.01, "key1") == obj[3]["key1"]
int idx=lbc_tbvalsv(3,obj,0,2); // obj[3]=obj[0][2]={1,k="a\0z"} etc
// copy from idx[0][2] to dst idx[3]. dstidx allows only intnum.
// lbc_runXX(), directf() uses obj[0] as rtnbuff and overwrite.
idx = lbc_tbvalsv(-1, obj, 0, 1); //dstidx<0: use autoidx. rtn= -1
idx = lbc_tbvalsv(-9, obj, 0,"a"); //rtn=-2,obj[-2]=obj[0]["a"]=nil
idx = lbc_tbvalsv(-1, obj, 0, 2); //rtn= -2 (upper obj[-2]=nil)
lbc_delsv(obj, -2); // obj[-2]=nil
lbc_cleansv(obj); // del all autoidx, obj[idx<0]
idx = lbc_tbvalsv(-7,obj,0); //obj[-1]=obj[0],rtn= -1
// you must manage saveidx yourself if use idx>0
// you may use autoidx for local val or tmpbuff pool etc.
//------support api usage
//mimic the lua forloop, lbc_pairs/ipairs/keysv/valsv (macro/funcs)
//(k,v,obj,0,"key") >> obj[0]["key"] ...you cant use dbl as keyidx
lbc_pairs(k, v, obj, 0){
printf("%c, %c\n", k.tp, v.tp); // obj[0] must be tb
printf("%d, %d\n", k.i, v.i); //k,v holds lbc_rt data
}
lbc_ipairs(num, val, obj, -3){
printf("%d,%c\n",num,val.tp); //obj[-3], ipair key is int
int idx;
idx=lbc_keysv(obj, -8); //obj[autoidx]=keydata, no needs ag 'num'
idx=lbc_valsv(obj, 2 ); //obj[2]=obj[-3][keydata]
if(num>3){break;}
}
lbc_rt ck=lbc_tbval(obj, 2); //ck.i==123 etc. saved at pairs/ipairs
//---
//you can use lbc_directf() after lbc_runXX()
//almost the same as lbc_runstr()
//1: no argc, argv
//2: share the luajit global env. share the _G value
//3: lua_pushfstring() fmt, restricted printf() fmt. see lua manual
//4: rtn is saved to obj[0]
n = lbc_directf(obj, "print('hw'); return 10,%d", 20); //>> hw, n=2
val = lbc_tbval(obj, 0, 2); //>> retuns to obj[0], val.i==20
//you can access/edit obj[0][2] to use obj->glname as follows
lbc_directf(obj,"print(%s[0][2]);return 1,'gw'",obj->glname); //20
val = lbc_tbval(obj, 0, 2); // val.s == "gw", overwrote obj[0]
//prunbcM(), prunstr(), pdirectf() works as pcall()
//catches inner error, assert() and 'return emsg' , returns 1 str if err.
n= lbc_pdirectf(obj,"assert(nil,'abc')"); // err: n= -1, rtnsz=1
if(n<0){ val = lbc_tbval(obj, 0, 1); } // val.s == "abc", errmsg.
// rc 'n' is the same as runbcM(), count of return args if noerr
lbc_free(obj);
//----
//--luahash, lhh is independent from lbc, lhh->L != lbc->L
//keystr cant holds '\0' but valstr can if set ag4.
lhh_t* rec = lhh_new();
int sum = lhh_set(rec,1,10); // rec[1]=10, sum=1(cnt holds data)
lhh_set(rec, "abc", -3); // rec["abc"] = -3
lhh_set(rec, 11, "xy") ; // rec[11] = "xy", sum=2
lhh_set(rec,"a","a\0b",3); // rec["a"]=a(\0)b, sz=3 (ag4)
const char* ky = "abc";
lhh_set(rec, &ky, -4); // rec["abc"] = -4, sum=4, overwrite
//args numtype is int in dfl. XXd treats args as double type.
//set the correct type/value or cast (int) (double) if use numarg
lhh_setd(rec, 2.2, "a"); //rec[2.2]="a", setd() uses k/v dbl
lhh_setd(rec, (double)2, 2.7); // rec[2]=2.7, treated by the luarule
lhh_unsetd(rec, 2.2); //rec[2.2]=nil, unset()/unsetd()
lhh_unset(rec, "nosetkey"); //noerr
//get
lhh_rt v = lhh_get(rec, "abc"); // v.tp='n', v.i= -4, v.tp/i/d/s/sz
v = lhh_getd(rec, (double)11); // v.tp='s', v.s="xy", v.sz=2
v = lhh_get(rec, "a"); // v.tp='s', v.sz=3
//lhh can borrow lbc pairloop
lbc_pairs(k, v, rec){
printf("%s/%d %s/%d\n", k.s?k.s:"", k.i, v.s?v.s:"", v.i);
lhh_unset(rec, 1); // ok, del in loop
lhh_set(rec, 11, "xxyz"); // ok, replace in loop
//lhh_set(rec, 99, 1);
//NG, add newval. use other buff, lhh_set(rec2, 99, 1) etc
//lhh_get(rec, 1);
//NG, get() breaks stack sequence. use k.i, v.i etc
}
lhh_free(rec);
return 0;
}
//~$ gcc src2.c -lluajit5.1-a -lm -ldl liblbc.a
//if you use valgrind memcheck, use -static to avoid miscount.
//~$ gcc src2.c -static liblbc.a -lm -ldl
-
-
int lbc_reqbcM() : suc/fail == 0/not0
int lbc_delsv() : same
int lbc_cleansv(): same
int lbc_runbcM() : cnt return args. eg) (lua)return 0,"a",{1,2} >> 3
int lbc_runstr() : same
int lbc_directf(): same
int lbc_prunbcM(): same if no err. return -1 and set one emsg if err.
int lbc_prunstr(): same
int lbc_pdirectf(): same
int lbc_tbvalsv(): savedist index
int lbc_keysv() : same
int lbc_valsv() : same
int lhh_set() : count of all holding keys
int lhh_setd() : same
int lhh_unset(): same
int lhh_unsetd(): same
output emsg and exit(1)
- sloppy benchmark:
--luajit vs lbc
lj : for i=1,num do local local v=i+1 end
C : while(num){ lbc_tbvalsv(2, obj, 0, 1); }
//lj : real 154.00ms : loop: 100*1000*1000: (1)
//lbc: real 2943.116 ms : loop: 1*1000*1000: 20x100 == (2000)
--c99 hash vs lhh
FAST: c_arr,lj(1) > c99hash(2) >> lhh(10) >>> lbc(200) :SLOW
- c99 posix hsearch()
real 46.685 ms : c99setloop
real 44.315 ms : c99getloop
- c arr[], arr[10]=100 etc
real 21.068 ms : arr, setloop
real 16.977 ms : arr, getloop
- luajit, table/arr tb[123]="abc" etc
set/get 22ms
- lhh() lhh_set(obj,"key",10) etc
real 197.392 ms : lhh, setloop
real 192.476 ms : lhh, getloop
- lbc_directf(), "local tb[123]=456; return" etc
real 3936.665 ms: .directf
..luajit vm is very fast but lua_stack() handling cause bottlenecks. maybe you
can bypass it with luajit ffi() cdata, direct pointer access.
(https://luajit.org/ext_ffi_tutorial.html, Cast pointer to address)
normal usage use ffi() cdata
-----+ +----- -----+ +-----
| push/pop | lj() +----------+ C_func()
lj() +----------+ C_func() +----------+
+----------+ | |
| | cdata <--------------> arr[]
-----+ +----- -----+ +-----
--- bypass code: lj >>> C
const char* cmd =
" ffi = require('ffi')" //head "(space)", lua is free format
" local tb = {}; for i=1,1000 do tb[#tb+1]=i end"
" local box= ffi.new('int[?]', #tb, tb )"
" local adrs= " LBC_C2P("box")
//-- macro 'LBC_C2P()' works as below, cdata to ptrnum
//" local adrs=tonumber(ffi.cast('intptr_t',ffi.cast('void *',box)))"
" return adrs, box" //holds datasrc 'box' to protect from gc.
;
lbc_directf(obj, "%s", cmd);
lbc_rt res = lbc_tbval(obj, 0, 1); //adrs
int* arr = (int*)(intptr_t)res.d; // adrs(number) >> arr(ptr)
printf("%d %d\n", arr[0], arr[1]); //>>1,2
--- bypass code: lj <<< C
#include <inttypes.h>
const char* s = "a\0bc";
const char* fmt = " ffi = require('ffi')"
" local str = " LBC_P2S
//-- macro 'LBC_P2S' works as printf fmt. needs ptr + sz
// " local cdata = ffi.new('uintptr_t', %" PRIuPTR ")"
// " cdata = ffi.cast('const char*', cdata)"
// " local str = ffi.string(cdata, %d)"
//-- macro 'LBC_P2C' works as below. needs ptr only
// " local cdata =" LBC_P2C
// " cdata = ffi.cast(void*, ffi.new('uintptr_t', %" PRIuPTR "))"
" return str..'xyz'" //gc deletes cdata. res is copied to 'str'
;
lbc_directf(obj, fmt, s, 4); // ptr,binsz == "a\0bc", 4
res = lbc_tbval(obj, 0, 1);
printf("%c %s %s, %d\n", res.tp,res.s,res.s+2,res.sz);
// 's', a(\0bcxyz), bcxyz, 7
lbc_free(obj);
// num<->ptr cast allows only throught intptr_t/uintptr_t in luajit.
// printf("%p\n", p) / printf("%" PRIuPTR "\n", p) work allmost the same
// the latter is more portable (c99 defined)
// ffi.string(cdata, len) can get str/bin[len] data from C
--- concept
- use luajit code as __asm__ in C
- i want to embed normal luajit src.lua directly to C src
- no glue code, glue file
- get luacode result without complex stack handling
- output C code as 1 file without dependent files (a.out, libX.so etc)
- easy api, low learning cost
- portable, posix
POSIX.1-2001+
Copyright 2021 momi-g, GPLv3+
2021-08-28 v1.1.1
https://www.lua.org/
https://luajit.org/
https://stackoverflow.com/questions/2632300
https://stackoverflow.com/questions/44479282