LBC(3) Library Functions Manual LBC(3)

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