summaryrefslogtreecommitdiff
path: root/lua_json_encode.c
diff options
context:
space:
mode:
Diffstat (limited to 'lua_json_encode.c')
-rw-r--r--lua_json_encode.c256
1 files changed, 256 insertions, 0 deletions
diff --git a/lua_json_encode.c b/lua_json_encode.c
new file mode 100644
index 0000000..201f769
--- /dev/null
+++ b/lua_json_encode.c
@@ -0,0 +1,256 @@
1/*
2 * Lua JSON routines
3 *
4 * CAVEATS:
5 * - JSON "null" handling:
6 * - Decoding a "null" in an array will leave a "nil" placeholder in Lua, but will not show up at the end of the array.
7 * - Decoding a "null" in an object will ensure that particular key is deleted in the Lua table.
8 */
9
10#include <string.h>
11#include <math.h>
12
13#include <lua.h>
14#include <lauxlib.h>
15#include <json/json.h>
16
17#include "lua_json.h"
18#include "utils.h"
19#include "str.h"
20
21/* FIXME:
22 * - Don't just pushnil on error and return?
23 * - Review all strbuf usage for NULL termination
24 */
25
26/* JSON escape a character if required, or return NULL */
27static inline char *json_escape_char(int c)
28{
29 switch(c) {
30 case 0:
31 return "\\u0000";
32 case '\\':
33 return "\\\\";
34 case '"':
35 return "\\\"";
36 case '\b':
37 return "\\b";
38 case '\t':
39 return "\\t";
40 case '\n':
41 return "\\n";
42 case '\f':
43 return "\\f";
44 case '\r':
45 return "\\r";
46 }
47
48 return NULL;
49}
50
51/* FIXME:
52 * - Use lua_checklstring() instead of lua_tolstring() ?*
53 */
54
55/* FIXME:
56 * - Option to encode non-printable characters? Only \" \\ are required
57 * - Unicode?
58 * - Improve performance?
59 */
60static void json_append_string(lua_State *l, struct str *json, int index)
61{
62 char *p;
63 int i;
64 const char *str;
65 size_t len;
66
67 str = lua_tolstring(l, index, &len);
68
69 strbuf_append_char(json, '\"');
70 for (i = 0; i < len; i++) {
71 p = json_escape_char(str[i]);
72 if (p)
73 strbuf_append_mem(json, p, strlen(p));
74 else
75 strbuf_append_char(json, str[i]);
76 }
77 strbuf_append_char(json, '\"');
78}
79
80/* Find the size of the array on the top of the Lua stack
81 * -1 object
82 * >=0 elements in array
83 */
84static int lua_array_length(lua_State *l)
85{
86 double k;
87 int max;
88
89 max = 0;
90
91 lua_pushnil(l);
92 /* table, startkey */
93 while (lua_next(l, -2) != 0) {
94 /* table, key, value */
95 if ((k = lua_tonumber(l, -2))) {
96 /* Integer >= 1 ? */
97 if (floor(k) == k && k >= 1) {
98 if (k > max)
99 max = k;
100 lua_pop(l, 1);
101 continue;
102 }
103 }
104
105 /* Must not be an array (non integer key) */
106 lua_pop(l, 2);
107 return -1;
108 }
109
110 return max;
111}
112
113static void json_append_data(lua_State *l, struct str *s);
114
115static void json_append_array(lua_State *l, struct str *s, int size)
116{
117 int comma, i;
118
119 strbuf_append_mem(s, "[ ", 2);
120
121 comma = 0;
122 for (i = 1; i <= size; i++) {
123 if (comma)
124 strbuf_append_mem(s, ", ", 2);
125 else
126 comma = 1;
127
128 lua_rawgeti(l, -1, i);
129 json_append_data(l, s);
130 lua_pop(l, 1);
131 }
132
133 strbuf_append_mem(s, " ]", 2);
134}
135
136static void json_append_object(lua_State *l, struct str *s)
137{
138 int comma, keytype;
139
140 /* Object */
141 strbuf_append_mem(s, "{ ", 2);
142
143 lua_pushnil(l);
144 /* table, startkey */
145 comma = 0;
146 while (lua_next(l, -2) != 0) {
147 if (comma)
148 strbuf_append_mem(s, ", ", 2);
149 else
150 comma = 1;
151
152 /* table, key, value */
153 keytype = lua_type(l, -2);
154 if (keytype == LUA_TNUMBER) {
155 strbuf_append(s, "\"" LUA_NUMBER_FMT "\": ", lua_tonumber(l, -2));
156 } else if (keytype == LUA_TSTRING) {
157 json_append_string(l, s, -2);
158 strbuf_append_mem(s, ": ", 2);
159 } else {
160 die("Cannot serialise table key %s", lua_typename(l, lua_type(l, -2)));
161 }
162
163 /* table, key, value */
164 json_append_data(l, s);
165 lua_pop(l, 1);
166 /* table, key */
167 }
168
169 strbuf_append_mem(s, " }", 2);
170}
171
172/* Serialise Lua data into JSON string.
173 *
174 * FIXME:
175 * - Error handling when cannot serialise key or value (return to script)
176 */
177static void json_append_data(lua_State *l, struct str *s)
178{
179 int len;
180
181 switch (lua_type(l, -1)) {
182 case LUA_TSTRING:
183 json_append_string(l, s, -1);
184 break;
185 case LUA_TNUMBER:
186 strbuf_append(s, "%lf", lua_tonumber(l, -1));
187 break;
188 case LUA_TBOOLEAN:
189 if (lua_toboolean(l, -1))
190 strbuf_append_mem(s, "true", 4);
191 else
192 strbuf_append_mem(s, "false", 5);
193 break;
194 case LUA_TTABLE:
195 len = lua_array_length(l);
196 if (len >= 0)
197 json_append_array(l, s, len);
198 else
199 json_append_object(l, s);
200 break;
201 case LUA_TNIL:
202 strbuf_append_mem(s, "null", 4);
203 break;
204 default:
205 /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, and LUA_TLIGHTUSERDATA)
206 * cannot be serialised */
207 /* FIXME: return error */
208 die("Cannot serialise %s", lua_typename(l, lua_type(l, -1)));
209 }
210}
211
212char *lua_to_json(lua_State *l, int *len)
213{
214 struct str *s;
215 char *data;
216
217 s = strbuf_new();
218 strbuf_set_increment(s, 256);
219 json_append_data(l, s);
220 data = strbuf_to_char(s, len);
221
222 return data;
223}
224
225int lua_json_encode(lua_State *l)
226{
227 char *json;
228 int len;
229
230 json = lua_to_json(l, &len);
231 lua_pushlstring(l, json, len);
232 free(json);
233
234 return 1;
235}
236
237void lua_json_init(lua_State *l)
238{
239 luaL_Reg reg[] = {
240 { "encode", lua_json_encode },
241 { "decode", lua_json_decode },
242 { NULL, NULL }
243 };
244
245 /* Create "db" table.
246 * Added functions as table entries
247 */
248
249 luaL_register(l, "json", reg);
250
251 /* FIXME: Debugging */
252 json_init_lookup_tables();
253}
254
255/* vi:ai et sw=4 ts=4:
256 */