aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Pulford <mark@kyne.com.au>2011-04-15 23:37:41 +0930
committerMark Pulford <mark@kyne.com.au>2011-04-15 23:37:41 +0930
commit9f3d6b59c5f097d66e94f987c7731d7a4113057f (patch)
tree4a3b0aa4f59a0b645b0cfb12fc89d260f1fca9b8
parenta99fc753a590d7dd1cf19d083af504cb3ec9a8d4 (diff)
downloadlua-cjson-9f3d6b59c5f097d66e94f987c7731d7a4113057f.tar.gz
lua-cjson-9f3d6b59c5f097d66e94f987c7731d7a4113057f.tar.bz2
lua-cjson-9f3d6b59c5f097d66e94f987c7731d7a4113057f.zip
Merge lua_json_encode/decode.c into lua_json.c
-rw-r--r--lua_json.c (renamed from lua_json_decode.c)301
-rw-r--r--lua_json_encode.c256
2 files changed, 282 insertions, 275 deletions
diff --git a/lua_json_decode.c b/lua_json.c
index ae35574..ab72dd5 100644
--- a/lua_json_decode.c
+++ b/lua_json.c
@@ -1,13 +1,12 @@
1#include <assert.h> 1/* Lua JSON routines
2#include <string.h> 2 */
3#include <lua.h>
4#include <lauxlib.h>
5#include "strbuf.h"
6 3
7/* Caveats: 4/* Caveats:
8 * - NULL values do not work in objects (unssuported by Lua tables). 5 * - No unicode support
9 * - Could use a secial "null" table object, that is unique 6 * - JSON "null" values are represented as lightuserdata. Compare with
10 * - NULL values work in arrays (probably not at the end) 7 * json.null.
8 * - Parsing comments is not support. According to json.org, this isn't
9 * part of the spec.
11 */ 10 */
12 11
13/* FIXME: 12/* FIXME:
@@ -16,9 +15,252 @@
16 * - Use lua_checkstack() to ensure there is enough stack space left to 15 * - Use lua_checkstack() to ensure there is enough stack space left to
17 * fulfill an operation. What happens if we don't, is that acceptible too? 16 * fulfill an operation. What happens if we don't, is that acceptible too?
18 * Does lua_checkstack grow the stack, or merely check if it is possible? 17 * Does lua_checkstack grow the stack, or merely check if it is possible?
19 * - Merge encode/decode files
20 */ 18 */
21 19
20/* FIXME:
21 * - Option to encode non-printable characters? Only \" \\ are required
22 * - Unicode?
23 */
24
25/* FIXME:
26 * - Review memory allocation handling and error returns.
27 * Ensure all memory is free. Including after exceptions.
28 */
29
30#include <assert.h>
31#include <string.h>
32#include <math.h>
33
34#include <lua.h>
35#include <lauxlib.h>
36
37#include "lua_json.h"
38#include "utils.h"
39#include "strbuf.h"
40
41/* ===== ENCODING ===== */
42
43
44/* JSON escape a character if required, or return NULL */
45static inline char *json_escape_char(int c)
46{
47 switch(c) {
48 case 0:
49 return "\\u0000";
50 case '\\':
51 return "\\\\";
52 case '"':
53 return "\\\"";
54 case '\b':
55 return "\\b";
56 case '\t':
57 return "\\t";
58 case '\n':
59 return "\\n";
60 case '\f':
61 return "\\f";
62 case '\r':
63 return "\\r";
64 }
65
66 return NULL;
67}
68
69/* json_append_string args:
70 * - lua_State
71 * - JSON strbuf
72 * - String (Lua stack index)
73 *
74 * Returns nothing. Doesn't remove string from Lua stack */
75static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
76{
77 char *p;
78 int i;
79 const char *str;
80 size_t len;
81
82 str = lua_tolstring(l, lindex, &len);
83
84 strbuf_append_char(json, '\"');
85 for (i = 0; i < len; i++) {
86 p = json_escape_char(str[i]);
87 if (p)
88 strbuf_append_string(json, p);
89 else
90 strbuf_append_char(json, str[i]);
91 }
92 strbuf_append_char(json, '\"');
93}
94
95/* Find the size of the array on the top of the Lua stack
96 * -1 object (not a pure array)
97 * >=0 elements in array
98 */
99static int lua_array_length(lua_State *l)
100{
101 double k;
102 int max;
103
104 max = 0;
105
106 lua_pushnil(l);
107 /* table, startkey */
108 while (lua_next(l, -2) != 0) {
109 /* table, key, value */
110 if ((k = lua_tonumber(l, -2))) {
111 /* Integer >= 1 ? */
112 if (floor(k) == k && k >= 1) {
113 if (k > max)
114 max = k;
115 lua_pop(l, 1);
116 continue;
117 }
118 }
119
120 /* Must not be an array (non integer key) */
121 lua_pop(l, 2);
122 return -1;
123 }
124
125 return max;
126}
127
128static void json_append_data(lua_State *l, strbuf_t *json);
129
130/* json_append_array args:
131 * - lua_State
132 * - JSON strbuf
133 * - Size of passwd Lua array (top of stack) */
134static void json_append_array(lua_State *l, strbuf_t *json, int array_length)
135{
136 int comma, i;
137
138 strbuf_append_string(json, "[ ");
139
140 comma = 0;
141 for (i = 1; i <= array_length; i++) {
142 if (comma)
143 strbuf_append_string(json, ", ");
144 else
145 comma = 1;
146
147 lua_rawgeti(l, -1, i);
148 json_append_data(l, json);
149 lua_pop(l, 1);
150 }
151
152 strbuf_append_string(json, " ]");
153}
154
155static void json_append_object(lua_State *l, strbuf_t *json)
156{
157 int comma, keytype;
158
159 /* Object */
160 strbuf_append_string(json, "{ ");
161
162 lua_pushnil(l);
163 /* table, startkey */
164 comma = 0;
165 while (lua_next(l, -2) != 0) {
166 if (comma)
167 strbuf_append_string(json, ", ");
168 else
169 comma = 1;
170
171 /* table, key, value */
172 keytype = lua_type(l, -2);
173 if (keytype == LUA_TNUMBER) {
174 strbuf_append_fmt(json, "\"" LUA_NUMBER_FMT "\": ",
175 lua_tonumber(l, -2));
176 } else if (keytype == LUA_TSTRING) {
177 json_append_string(l, json, -2);
178 strbuf_append_string(json, ": ");
179 } else {
180 die("Cannot serialise table key %s", lua_typename(l, lua_type(l, -2)));
181 }
182
183 /* table, key, value */
184 json_append_data(l, json);
185 lua_pop(l, 1);
186 /* table, key */
187 }
188
189 strbuf_append_string(json, " }");
190}
191
192/* Serialise Lua data into JSON string.
193 *
194 * FIXME:
195 * - Error handling when cannot serialise key or value (return to script)
196 */
197static void json_append_data(lua_State *l, strbuf_t *json)
198{
199 int len;
200
201 switch (lua_type(l, -1)) {
202 case LUA_TSTRING:
203 json_append_string(l, json, -1);
204 break;
205 case LUA_TNUMBER:
206 strbuf_append_fmt(json, "%lf", lua_tonumber(l, -1));
207 break;
208 case LUA_TBOOLEAN:
209 if (lua_toboolean(l, -1))
210 strbuf_append_string(json, "true");
211 else
212 strbuf_append_string(json, "false");
213 break;
214 case LUA_TTABLE:
215 len = lua_array_length(l);
216 if (len >= 0)
217 json_append_array(l, json, len);
218 else
219 json_append_object(l, json);
220 break;
221 case LUA_TNIL:
222 strbuf_append_string(json, "null");
223 break;
224 case LUA_TLIGHTUSERDATA:
225 if (lua_touserdata(l, -1) == NULL) {
226 strbuf_append_string(json, "null");
227 break;
228 }
229 default:
230 /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, and LUA_TLIGHTUSERDATA)
231 * cannot be serialised */
232 /* FIXME: return error */
233 die("Cannot serialise %s", lua_typename(l, lua_type(l, -1)));
234 }
235}
236
237char *lua_json_encode(lua_State *l, int *len)
238{
239 strbuf_t buf;
240 char *json;
241
242 strbuf_init(&buf);
243 strbuf_set_increment(&buf, 256);
244 json_append_data(l, &buf);
245 json = strbuf_free_to_string(&buf, len);
246
247 return json;
248}
249
250int lua_api_json_encode(lua_State *l)
251{
252 char *json;
253 int len;
254
255 json = lua_json_encode(l, &len);
256 lua_pushlstring(l, json, len);
257 free(json);
258
259 return 1;
260}
261
262/* ===== DECODING ===== */
263
22typedef struct { 264typedef struct {
23 const char *data; 265 const char *data;
24 int index; 266 int index;
@@ -76,7 +318,7 @@ static void json_process_value(lua_State *l, json_parse_t *json, json_token_t *t
76static json_token_type_t json_ch2token[256]; 318static json_token_type_t json_ch2token[256];
77static char json_ch2escape[256]; 319static char json_ch2escape[256];
78 320
79void json_init_lookup_tables() 321static void json_init_lookup_tables()
80{ 322{
81 int i; 323 int i;
82 324
@@ -163,7 +405,7 @@ static void json_next_string_token(json_parse_t *json, json_token_t *token)
163 strbuf_ensure_null(json->tmp); 405 strbuf_ensure_null(json->tmp);
164 406
165 token->type = T_STRING; 407 token->type = T_STRING;
166 token->value.string = json->tmp->data; 408 token->value.string = strbuf_string(json->tmp, NULL);
167 token->length = json->tmp->length; 409 token->length = json->tmp->length;
168} 410}
169 411
@@ -221,9 +463,9 @@ static void json_next_token(json_parse_t *json, json_token_t *token)
221 return; 463 return;
222 } 464 }
223 465
466 /* Process characters which triggered T_UNKNOWN */
224 ch = json->data[json->index]; 467 ch = json->data[json->index];
225 468
226 /* Process characters which triggered T_UNKNOWN */
227 if (ch == '"') { 469 if (ch == '"') {
228 json_next_string_token(json, token); 470 json_next_string_token(json, token);
229 return; 471 return;
@@ -298,8 +540,7 @@ static void json_parse_object_context(lua_State *l, json_parse_t *json)
298 json_throw_parse_error(l, json, "comma or object end", &token); 540 json_throw_parse_error(l, json, "comma or object end", &token);
299 541
300 json_next_token(json, &token); 542 json_next_token(json, &token);
301 } while (1); 543 }
302
303} 544}
304 545
305/* Handle the array context */ 546/* Handle the array context */
@@ -354,7 +595,9 @@ static void json_process_value(lua_State *l, json_parse_t *json, json_token_t *t
354 json_parse_array_context(l, json); 595 json_parse_array_context(l, json);
355 break;; 596 break;;
356 case T_NULL: 597 case T_NULL:
357 lua_pushnil(l); 598 /* In Lua, setting "t[k] = nil" will delete k from the table.
599 * Hence a NULL pointer lightuserdata object is used instead */
600 lua_pushlightuserdata(l, NULL);
358 break;; 601 break;;
359 default: 602 default:
360 json_throw_parse_error(l, json, "value", token); 603 json_throw_parse_error(l, json, "value", token);
@@ -362,7 +605,7 @@ static void json_process_value(lua_State *l, json_parse_t *json, json_token_t *t
362} 605}
363 606
364/* json_text must be null terminated string */ 607/* json_text must be null terminated string */
365void json_parse(lua_State *l, const char *json_text) 608void lua_json_decode(lua_State *l, const char *json_text)
366{ 609{
367 json_parse_t json; 610 json_parse_t json;
368 json_token_t token; 611 json_token_t token;
@@ -370,7 +613,7 @@ void json_parse(lua_State *l, const char *json_text)
370 json.data = json_text; 613 json.data = json_text;
371 json.index = 0; 614 json.index = 0;
372 json.tmp = strbuf_new(); 615 json.tmp = strbuf_new();
373 json.tmp->scale = 256; 616 strbuf_set_increment(json.tmp, 256);
374 617
375 json_next_token(&json, &token); 618 json_next_token(&json, &token);
376 json_process_value(l, &json, &token); 619 json_process_value(l, &json, &token);
@@ -384,7 +627,7 @@ void json_parse(lua_State *l, const char *json_text)
384 strbuf_free(json.tmp); 627 strbuf_free(json.tmp);
385} 628}
386 629
387int lua_json_decode(lua_State *l) 630static int lua_api_json_decode(lua_State *l)
388{ 631{
389 int i, n; 632 int i, n;
390 633
@@ -392,7 +635,7 @@ int lua_json_decode(lua_State *l)
392 635
393 for (i = 1; i <= n; i++) { 636 for (i = 1; i <= n; i++) {
394 if (lua_isstring(l, i)) { 637 if (lua_isstring(l, i)) {
395 json_parse(l, lua_tostring(l, i)); 638 lua_json_decode(l, lua_tostring(l, i));
396 } else { 639 } else {
397 lua_pushnil(l); 640 lua_pushnil(l);
398 } 641 }
@@ -401,5 +644,25 @@ int lua_json_decode(lua_State *l)
401 return n; /* Number of results */ 644 return n; /* Number of results */
402} 645}
403 646
647/* ===== INITIALISATION ===== */
648
649void lua_json_init(lua_State *l)
650{
651 luaL_Reg reg[] = {
652 { "encode", lua_api_json_encode },
653 { "decode", lua_api_json_decode },
654 { NULL, NULL }
655 };
656
657 json_init_lookup_tables();
658
659 luaL_register(l, "json", reg);
660
661 /* Set json.null, and pop "json" table from the stack */
662 lua_pushlightuserdata(l, NULL);
663 lua_setfield(l, -2, "null");
664 lua_pop(l, 1);
665}
666
404/* vi:ai et sw=4 ts=4: 667/* vi:ai et sw=4 ts=4:
405 */ 668 */
diff --git a/lua_json_encode.c b/lua_json_encode.c
deleted file mode 100644
index 201f769..0000000
--- a/lua_json_encode.c
+++ /dev/null
@@ -1,256 +0,0 @@
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 */