1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
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
|