-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathhvlua_json.cpp
More file actions
205 lines (186 loc) · 6.44 KB
/
Copy pathhvlua_json.cpp
File metadata and controls
205 lines (186 loc) · 6.44 KB
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
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}
#include "hvlua.h"
#include "hvlua_json.h" // shared lua<->json conversion (also used by HttpLuaHandler)
#include <string>
#include "hstring.h"
using nlohmann::json;
// The lua <-> json conversion is defined here (non-static, in namespace hv) as
// the single shared implementation; http/server/HttpLuaHandler.cpp reuses it via
// hvlua_json.h. Only hvlua_open_json is exported with C linkage for hvlua.c.
namespace hv {
json hvlua_lua_to_json(lua_State* L, int index, int depth);
static json lua_table_to_json(lua_State* L, int index, int depth) {
index = lua_absindex(L, index);
bool is_array = true;
lua_Integer max_index = 0;
size_t count = 0;
lua_pushnil(L);
while (lua_next(L, index) != 0) {
++count;
if (lua_type(L, -2) == LUA_TNUMBER && lua_isinteger(L, -2)) {
lua_Integer k = lua_tointeger(L, -2);
if (k <= 0) is_array = false;
else if (k > max_index) max_index = k;
} else {
is_array = false;
}
lua_pop(L, 1);
}
if (is_array && (lua_Integer)count == max_index) {
json j = json::array();
for (lua_Integer i = 1; i <= max_index; ++i) {
lua_geti(L, index, i);
j.push_back(hvlua_lua_to_json(L, -1, depth + 1));
lua_pop(L, 1);
}
return j;
}
json j = json::object();
lua_pushnil(L);
while (lua_next(L, index) != 0) {
std::string key;
if (lua_type(L, -2) == LUA_TSTRING) {
size_t len = 0;
const char* s = lua_tolstring(L, -2, &len);
key.assign(s, len);
} else if (lua_type(L, -2) == LUA_TNUMBER) {
key = hv::to_string((int64_t)lua_tointeger(L, -2));
}
if (!key.empty()) j[key] = hvlua_lua_to_json(L, -1, depth + 1);
lua_pop(L, 1);
}
return j;
}
json hvlua_lua_to_json(lua_State* L, int index, int depth) {
switch (lua_type(L, index)) {
case LUA_TNIL: return nullptr;
case LUA_TBOOLEAN: return lua_toboolean(L, index) != 0;
case LUA_TNUMBER:
if (lua_isinteger(L, index)) return (int64_t)lua_tointeger(L, index);
return lua_tonumber(L, index);
case LUA_TSTRING: {
size_t len = 0;
const char* s = lua_tolstring(L, index, &len);
return std::string(s, len);
}
case LUA_TTABLE:
// Guard against cyclic / pathologically deep tables. Two limits:
// (1) a depth cap so a self-referential table (t.self=t) can't recurse
// forever; (2) lua_checkstack, because each level uses Lua stack
// slots (lua_next / lua_geti) and Lua only guarantees LUA_MINSTACK —
// deep nesting without reserving would overflow the value stack and
// corrupt Lua's table internals (crash in luaH_*/getgeneric).
if (depth >= HVLUA_JSON_MAX_DEPTH) return nullptr;
if (!lua_checkstack(L, 4)) return nullptr;
return lua_table_to_json(L, index, depth);
default: return nullptr;
}
}
static bool json_to_lua(lua_State* L, const json& j, int depth) {
switch (j.type()) {
case json::value_t::null:
lua_pushnil(L);
return true;
case json::value_t::boolean:
lua_pushboolean(L, j.get<bool>());
return true;
case json::value_t::number_integer:
lua_pushinteger(L, (lua_Integer)j.get<int64_t>());
return true;
case json::value_t::number_unsigned:
lua_pushinteger(L, (lua_Integer)j.get<uint64_t>());
return true;
case json::value_t::number_float:
lua_pushnumber(L, j.get<double>());
return true;
case json::value_t::string: {
const std::string& s = j.get_ref<const std::string&>();
lua_pushlstring(L, s.data(), s.size());
return true;
}
case json::value_t::array: {
if (depth >= HVLUA_JSON_MAX_DEPTH || !lua_checkstack(L, 4)) return false;
lua_createtable(L, (int)j.size(), 0);
int i = 1;
for (const auto& item : j) {
if (!json_to_lua(L, item, depth + 1)) return false;
lua_seti(L, -2, i++);
}
return true;
}
case json::value_t::object: {
if (depth >= HVLUA_JSON_MAX_DEPTH || !lua_checkstack(L, 4)) return false;
lua_createtable(L, 0, (int)j.size());
for (auto it = j.begin(); it != j.end(); ++it) {
lua_pushlstring(L, it.key().data(), it.key().size());
if (!json_to_lua(L, it.value(), depth + 1)) return false;
lua_settable(L, -3);
}
return true;
}
default:
lua_pushnil(L);
return true;
}
}
bool hvlua_json_to_lua(lua_State* L, const json& j, int depth) {
int top = lua_gettop(L);
if (json_to_lua(L, j, depth)) return true;
lua_settop(L, top);
return false;
}
} // namespace hv
// hv.json.encode(value) -> string | nil, err
static int l_hv_json_encode(lua_State* L) {
json j = hv::hvlua_lua_to_json(L, 1, 0);
// Lua strings are arbitrary byte strings; nlohmann throws type_error.316 on
// invalid UTF-8. Catch it (and any other dump error) and return (nil, err)
// instead of letting the exception abort the process — this path is
// reachable from untrusted input (e.g. an HTTP handler doing ctx:json).
try {
std::string s = j.dump();
lua_pushlstring(L, s.data(), s.size());
return 1;
} catch (const std::exception& e) {
lua_pushnil(L);
lua_pushstring(L, e.what());
return 2;
}
}
// hv.json.decode(string) -> value | nil,err
static int l_hv_json_decode(lua_State* L) {
size_t len = 0;
const char* s = luaL_checklstring(L, 1, &len);
json j = json::parse(s, s + len, nullptr, false);
if (j.is_discarded()) {
lua_pushnil(L);
lua_pushstring(L, "json parse error");
return 2;
}
if (!hv::hvlua_json_to_lua(L, j)) {
lua_pushnil(L);
lua_pushstring(L, "json too deep");
return 2;
}
return 1;
}
static const luaL_Reg hv_json_funcs[] = {
{ "encode", l_hv_json_encode },
{ "decode", l_hv_json_decode },
{ NULL, NULL }
};
// Add the hv.json subtable to the (already created) global "hv" table.
extern "C" void hvlua_open_json(lua_State* L) {
lua_getglobal(L, "hv");
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
lua_newtable(L);
}
luaL_newlib(L, hv_json_funcs);
lua_setfield(L, -2, "json");
lua_setglobal(L, "hv");
}