summaryrefslogtreecommitdiffstats
path: root/src/common/impl/lua.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/impl/lua.c')
-rw-r--r--src/common/impl/lua.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/common/impl/lua.c b/src/common/impl/lua.c
new file mode 100644
index 0000000..ffc1d83
--- /dev/null
+++ b/src/common/impl/lua.c
@@ -0,0 +1,233 @@
+#if FF_HAVE_LUA
+
+ #include "common/lua.h"
+ #include "common/mallocHelper.h"
+
+struct FFLuaData luaData;
+
+static yyjson_mut_val* lua2yyjson(lua_State* L, int idx, yyjson_mut_doc* doc, int depth) {
+ if (__builtin_expect(depth > 15, false)) {
+ yyjson_mut_doc_free(doc);
+ lua_pushlstring(
+ L, "yyjson: recursion depth exceeded; possible circular reference", strlen("yyjson: recursion depth exceeded; possible circular reference"));
+ lua_error(L); // noreturn
+ __builtin_unreachable();
+ }
+
+ if (idx < 0) {
+ idx = lua_gettop(L) + idx + 1;
+ }
+ int type = lua_type(L, idx);
+
+ switch (type) {
+ case LUA_TNIL:
+ return yyjson_mut_null(doc);
+
+ case LUA_TBOOLEAN:
+ return yyjson_mut_bool(doc, lua_toboolean(L, idx));
+
+ case LUA_TNUMBER: {
+ if (lua_isinteger(L, idx)) {
+ lua_Integer i = lua_tointeger(L, idx);
+ return yyjson_mut_sint(doc, (int64_t) i);
+ } else {
+ return yyjson_mut_real(doc, lua_tonumber(L, idx));
+ }
+ }
+
+ case LUA_TSTRING: {
+ size_t len;
+ const char* str = lua_tolstring(L, idx, &len);
+ return yyjson_mut_strncpy(doc, str, len);
+ }
+
+ case LUA_TTABLE: {
+ lua_Unsigned len = lua_rawlen(L, idx);
+ int is_array = 1;
+
+ if (len == 0) {
+ lua_pushnil(L);
+ is_array = 0;
+ if (lua_next(L, idx) != 0) {
+ lua_pop(L, 2);
+ }
+ } else {
+ lua_pushnil(L);
+ while (lua_next(L, idx) != 0) {
+ if (lua_type(L, -2) != LUA_TNUMBER) {
+ is_array = 0;
+ lua_pop(L, 2);
+ break;
+ }
+ int isnum = false;
+ lua_Integer k = lua_tointegerx(L, -2, &isnum);
+ if (!isnum || (lua_Unsigned) k > len) {
+ is_array = 0;
+ lua_pop(L, 2);
+ break;
+ }
+ lua_pop(L, 1);
+ }
+ }
+
+ if (is_array) {
+ yyjson_mut_val* arr = yyjson_mut_arr(doc);
+ for (lua_Unsigned i = 1; i <= len; i++) {
+ lua_rawgeti(L, idx, (lua_Integer) i);
+ yyjson_mut_val* val = lua2yyjson(L, -1, doc, depth + 1);
+ yyjson_mut_arr_append(arr, val);
+ lua_pop(L, 1);
+ }
+ return arr;
+ } else {
+ yyjson_mut_val* obj = yyjson_mut_obj(doc);
+ lua_pushnil(L);
+ while (lua_next(L, idx) != 0) {
+ size_t klen;
+ const char* key_str = luaL_tolstring(L, -2, &klen);
+ yyjson_mut_val* key = yyjson_mut_strncpy(doc, key_str, klen);
+ lua_pop(L, 1);
+
+ yyjson_mut_val* val = lua2yyjson(L, -1, doc, depth + 1);
+ yyjson_mut_obj_add(obj, key, val);
+ lua_pop(L, 1);
+ }
+ return obj;
+ }
+ }
+
+ default:
+ return yyjson_mut_null(doc);
+ }
+}
+
+static int yyjsonEncode(lua_State* L) {
+ luaL_checkany(L, 1);
+
+ bool pretty = false;
+ if (lua_isboolean(L, 2)) {
+ pretty = (int) lua_toboolean(L, 2);
+ }
+
+ yyjson_mut_doc* doc = yyjson_mut_doc_new(NULL);
+ if (__builtin_expect(!doc, false)) {
+ lua_pushlstring(L, "yyjson: yyjson_mut_doc_new() failed", strlen("yyjson: yyjson_mut_doc_new() failed"));
+ return lua_error(L);
+ }
+
+ yyjson_mut_val* root = lua2yyjson(L, 1, doc, 0);
+ yyjson_mut_doc_set_root(doc, root);
+
+ size_t jsonLen;
+ yyjson_write_err err = {};
+ FF_AUTO_FREE const char* jsonStr = yyjson_mut_write_opts(doc, YYJSON_WRITE_ALLOW_INF_AND_NAN | (pretty ? YYJSON_WRITE_PRETTY_TWO_SPACES : 0), NULL, &jsonLen, &err);
+
+ if (__builtin_expect(jsonStr != NULL, true)) {
+ lua_pushlstring(L, jsonStr, jsonLen);
+ yyjson_mut_doc_free(doc);
+ return 1;
+ } else {
+ yyjson_mut_doc_free(doc);
+ {
+ FF_STRBUF_AUTO_DESTROY errBuf = ffStrbufCreateStatic("yyjson: yyjson_mut_write_opts() failed: ");
+ ffStrbufAppendS(&errBuf, err.msg);
+ lua_pushlstring(L, errBuf.chars, errBuf.length);
+ }
+
+ return lua_error(L); // longjmp
+ }
+}
+
+const char* ffLuaLoadState(void) {
+ if (luaData.inited) {
+ if (luaData.L == NULL) {
+ return "Lua library is not available";
+ }
+ return NULL;
+ }
+
+ luaData.inited = true;
+ // clang-format off
+ #ifdef _WIN32
+ #define FF_LOAD_LIBLUA(version) FF_LIBRARY_LOAD_MESSAGE(liblua, \
+ "lua5" #version FF_LIBRARY_EXTENSION, 0)
+ #else
+ #define FF_LOAD_LIBLUA(version) FF_LIBRARY_LOAD_MESSAGE(liblua, \
+ "liblua5." #version FF_LIBRARY_EXTENSION, 0, \
+ "liblua-5." #version FF_LIBRARY_EXTENSION, 0, \
+ "liblua5." #version FF_LIBRARY_EXTENSION ".5." #version, 0)
+ #endif
+ // clang-format on
+ #if LUA_VERSION_NUM == 505
+ FF_LOAD_LIBLUA(5)
+ #elif LUA_VERSION_NUM == 504
+ FF_LOAD_LIBLUA(4)
+ #elif LUA_VERSION_NUM == 503
+ FF_LOAD_LIBLUA(3)
+ #else
+ #error "Unsupported Lua version"
+ #endif
+ #undef FF_LOAD_LIBLUA
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaL_newstate)
+ #if LUA_VERSION_NUM >= 505
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaL_openselectedlibs)
+ #else
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaL_requiref)
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaopen_base)
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaopen_math)
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaopen_string)
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(liblua, luaopen_table)
+ #endif
+
+ #if !FF_DISABLE_DLOPEN
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, luaL_checkany)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, luaL_loadbufferx)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, luaL_tolstring)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_callk)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_createtable)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_error)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_gettop)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_isinteger)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_next)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pcallk)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushboolean)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushcclosure)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushinteger)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushlstring)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushnil)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushnumber)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_pushvalue)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_rawgeti)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_rawlen)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_setfield)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_setglobal)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_seti)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_settop)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_toboolean)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_tointegerx)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_tolstring)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_tonumberx)
+ FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(liblua, luaData, lua_type)
+ #endif
+
+ lua_State* L = ffluaL_newstate();
+ if (L == NULL) {
+ return "luaL_newstate() failed";
+ }
+ #if LUA_VERSION_NUM >= 505
+ ffluaL_openselectedlibs(L, LUA_GLIBK | LUA_MATHLIBK | LUA_STRLIBK | LUA_TABLIBK, 0);
+ #else
+ ffluaL_requiref(L, LUA_GNAME, ffluaopen_base, 1);
+ ffluaL_requiref(L, LUA_MATHLIBNAME, ffluaopen_math, 1);
+ ffluaL_requiref(L, LUA_STRLIBNAME, ffluaopen_string, 1);
+ ffluaL_requiref(L, LUA_TABLIBNAME, ffluaopen_table, 1);
+ lua_settop(L, 0);
+ #endif
+ lua_pushcfunction(L, yyjsonEncode);
+ lua_setglobal(L, "json_encode");
+ luaData.L = L;
+ liblua = NULL; // don't close lua
+ return NULL;
+}
+
+#endif