webmcp

view libraries/json/json.c @ 126:bccaa05aada7

Implemented efficient length operator for sparse JSON arrays (that may contain null values)
author jbe
date Sun Jul 27 03:54:39 2014 +0200 (2014-07-27)
parents e3e5bf890aad
children 83aced09adc7
line source
1 #include <lua.h>
2 #include <lauxlib.h>
3 #include <stdlib.h>
4 #include <string.h>
6 #define JSON_UPVAL_NULLS lua_upvalueindex(1)
7 #define JSON_UPVAL_METATABLE lua_upvalueindex(2)
9 #define JSON_STATE_VALUE 0
10 #define JSON_STATE_OBJECT_KEY 1
11 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2
12 #define JSON_STATE_OBJECT_VALUE 3
13 #define JSON_STATE_OBJECT_SEPARATOR 4
14 #define JSON_STATE_ARRAY_VALUE 5
15 #define JSON_STATE_ARRAY_SEPARATOR 6
16 #define JSON_STATE_END 7
18 static int json_import(lua_State *L) {
19 const char *str;
20 size_t total;
21 size_t pos = 0;
22 size_t level = 0;
23 int mode = JSON_STATE_VALUE;
24 char c;
25 luaL_Buffer luabuf;
26 char *cbuf;
27 size_t writepos;
28 int aryidx;
29 lua_settop(L, 1);
30 str = lua_tostring(L, 1);
31 total = strlen(str);
32 json_import_loop:
33 while (c = str[pos], c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f') pos++;
34 switch (c) {
35 case 0:
36 if (mode == JSON_STATE_END) return 1;
37 json_import_unexpected_eof:
38 lua_pushnil(L);
39 if (level == 0) lua_pushliteral(L, "Empty string");
40 else lua_pushliteral(L, "Unexpected end of JSON document");
41 return 2;
42 case '{':
43 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
44 goto json_import_syntax_error;
45 pos++;
46 lua_newtable(L); // the actual JSON object
47 lua_pushvalue(L, JSON_UPVAL_METATABLE);
48 lua_setmetatable(L, -2);
49 lua_newtable(L); // stores set of NULL values
50 lua_pushvalue(L, -2);
51 lua_pushvalue(L, -2);
52 lua_rawset(L, JSON_UPVAL_NULLS);
53 mode = JSON_STATE_OBJECT_KEY;
54 level++;
55 goto json_import_loop;
56 case '[':
57 if (mode != JSON_STATE_VALUE && mode != JSON_STATE_OBJECT_VALUE && mode != JSON_STATE_ARRAY_VALUE)
58 goto json_import_syntax_error;
59 pos++;
60 lua_newtable(L); // the actual JSON array
61 lua_pushvalue(L, JSON_UPVAL_METATABLE);
62 lua_setmetatable(L, -2);
63 lua_newtable(L); // stores set of NULL values
64 lua_pushvalue(L, -2);
65 lua_pushvalue(L, -2);
66 lua_rawset(L, JSON_UPVAL_NULLS);
67 lua_pushinteger(L, 0); // length of array (since it may contain nil's)
68 mode = JSON_STATE_ARRAY_VALUE;
69 level++;
70 goto json_import_loop;
71 case '}':
72 if (mode != JSON_STATE_OBJECT_KEY && mode != JSON_STATE_OBJECT_SEPARATOR)
73 goto json_import_syntax_error;
74 goto json_import_close;
75 case ']':
76 if (mode != JSON_STATE_ARRAY_VALUE && mode != JSON_STATE_ARRAY_SEPARATOR)
77 goto json_import_syntax_error;
78 lua_pop(L, 1); // pop length information
79 json_import_close:
80 pos++;
81 lua_pop(L, 1); // pop table that stores set of NULL values
82 if (--level) {
83 if (lua_type(L, -2) == LUA_TNUMBER) {
84 mode = JSON_STATE_ARRAY_VALUE;
85 } else {
86 mode = JSON_STATE_OBJECT_VALUE;
87 }
88 goto json_import_process_value;
89 } else {
90 mode = JSON_STATE_END;
91 }
92 goto json_import_loop;
93 case ':':
94 if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR)
95 goto json_import_syntax_error;
96 pos++;
97 mode = JSON_STATE_OBJECT_VALUE;
98 goto json_import_loop;
99 case ',':
100 if (mode == JSON_STATE_OBJECT_SEPARATOR) {
101 mode = JSON_STATE_OBJECT_KEY;
102 } else if (mode == JSON_STATE_ARRAY_SEPARATOR) {
103 mode = JSON_STATE_ARRAY_VALUE;
104 } else {
105 goto json_import_syntax_error;
106 }
107 pos++;
108 goto json_import_loop;
109 case '"':
110 cbuf = luaL_buffinitsize(L, &luabuf, total-pos);
111 writepos = 0;
112 pos++;
113 while ((c = str[pos++]) != '"') {
114 if (c == 0) {
115 goto json_import_unexpected_eof;
116 } else if (c < 32 || c == 127) {
117 lua_pushnil(L);
118 lua_pushliteral(L, "Unexpected control character in JSON string");
119 return 2;
120 } else if (c == '\\') {
121 c = str[pos++];
122 switch (c) {
123 case 0:
124 goto json_import_unexpected_eof;
125 case '"':
126 case '/':
127 case '\\':
128 cbuf[writepos++] = c;
129 break;
130 case 'b':
131 cbuf[writepos++] = '\b';
132 break;
133 case 'f':
134 cbuf[writepos++] = '\f';
135 break;
136 case 'n':
137 cbuf[writepos++] = '\n';
138 break;
139 case 'r':
140 cbuf[writepos++] = '\r';
141 break;
142 case 't':
143 cbuf[writepos++] = '\t';
144 break;
145 case 'u':
146 lua_pushnil(L);
147 lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet"); // TODO
148 return 2;
149 default:
150 lua_pushnil(L);
151 lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
152 return 2;
153 }
154 } else {
155 cbuf[writepos++] = c;
156 }
157 }
158 if (!c) goto json_import_unexpected_eof;
159 luaL_pushresultsize(&luabuf, writepos);
160 goto json_import_process_value;
161 }
162 if (c == '-' || (c >= '0' && c <= '9')) {
163 char *endptr;
164 double numval;
165 numval = strtod(str+pos, &endptr);
166 if (endptr == str+pos) goto json_import_syntax_error;
167 pos += endptr - (str+pos);
168 lua_pushnumber(L, numval);
169 } else if (!strncmp(str+pos, "true", 4)) {
170 lua_pushboolean(L, 1);
171 pos += 4;
172 } else if (!strncmp(str+pos, "false", 5)) {
173 lua_pushboolean(L, 0);
174 pos += 5;
175 } else if (!strncmp(str+pos, "null", 4)) {
176 lua_pushnil(L);
177 pos += 4;
178 } else {
179 goto json_import_syntax_error;
180 }
181 json_import_process_value:
182 switch (mode) {
183 case JSON_STATE_OBJECT_KEY:
184 if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
185 mode = JSON_STATE_OBJECT_KEY_TERMINATOR;
186 goto json_import_loop;
187 case JSON_STATE_OBJECT_VALUE:
188 if (lua_isnil(L, -1)) {
189 lua_pushvalue(L, -2);
190 lua_pushboolean(L, 1);
191 lua_rawset(L, -5);
192 }
193 lua_rawset(L, -4);
194 mode = JSON_STATE_OBJECT_SEPARATOR;
195 goto json_import_loop;
196 case JSON_STATE_ARRAY_VALUE:
197 aryidx = lua_tointeger(L, -2) + 1;
198 if (lua_isnil(L, -1)) {
199 lua_pushinteger(L, aryidx);
200 lua_pushboolean(L, 1);
201 lua_rawset(L, -5);
202 }
203 lua_rawseti(L, -4, aryidx);
204 lua_pop(L, 1);
205 lua_pushinteger(L, aryidx);
206 mode = JSON_STATE_ARRAY_SEPARATOR;
207 goto json_import_loop;
208 case JSON_STATE_VALUE:
209 mode = JSON_STATE_END;
210 goto json_import_loop;
211 }
212 json_import_syntax_error:
213 lua_pushnil(L);
214 lua_pushliteral(L, "Syntax error in JSON document");
215 return 2;
216 }
218 static int json_arylen(lua_State *L) {
219 int lower, middle;
220 int upper = 1;
221 // TODO: check input for valid array!
222 lua_settop(L, 1);
223 lua_pushvalue(L, 1);
224 lua_rawget(L, JSON_UPVAL_NULLS);
225 if (lua_isnil(L, -1)) goto json_arylen_default;
226 lua_pushnil(L);
227 if (!lua_next(L, -2)) goto json_arylen_default;
228 lua_pop(L, 2);
229 while (1) {
230 lua_rawgeti(L, 1, upper);
231 if (lua_isnil(L, -1)) {
232 // TODO: require metatable set?
233 lua_pop(L, 1);
234 lua_pushinteger(L, upper);
235 lua_rawget(L, 2);
236 }
237 if (lua_isnil(L, -1)) break;
238 lua_pop(L, 1);
239 upper *= 2;
240 // TODO: avoid integer overflow!
241 }
242 lua_pop(L, 1);
243 lower = upper / 2;
244 while (upper - lower > 1) {
245 middle = (lower + upper) / 2;
246 lua_rawgeti(L, 1, middle);
247 if (lua_isnil(L, -1)) {
248 // TODO: require metatable set?
249 lua_pop(L, 1);
250 lua_pushinteger(L, middle);
251 lua_rawget(L, 2);
252 }
253 if (lua_isnil(L, -1)) upper = middle;
254 else lower = middle;
255 lua_pop(L, 1);
256 }
257 lua_pushinteger(L, lower);
258 return 1;
259 json_arylen_default:
260 lua_pushinteger(L, lua_rawlen(L, 1));
261 return 1;
262 }
264 static int json_isnull(lua_State *L) {
265 // TODO: require metatable set?
266 lua_pushvalue(L, 1);
267 lua_rawget(L, JSON_UPVAL_NULLS);
268 if (lua_isnil(L, -1)) goto json_isnull_false;
269 lua_pushvalue(L, 2);
270 lua_rawget(L, -2);
271 if (!lua_isnil(L, -1)) return 1;
272 json_isnull_false:
273 lua_pushboolean(L, 0);
274 return 1;
275 }
277 static const struct luaL_Reg json_module_functions[] = {
278 {"import", json_import},
279 {"arylen", json_arylen},
280 {"isnull", json_isnull},
281 {NULL, NULL}
282 };
284 static const struct luaL_Reg json_metatable_functions[] = {
285 {"__len", json_arylen},
286 {NULL, NULL}
287 };
289 int luaopen_json(lua_State *L) {
290 lua_settop(L, 0);
291 lua_newtable(L); // 1: library table on stack position
292 lua_newtable(L); // 2: ephemeron table to store a set of keys associated with JSON-null values
293 lua_newtable(L); // 3: meta table for ephemeron table
294 lua_pushliteral(L, "__mode");
295 lua_pushliteral(L, "k");
296 lua_rawset(L, 3);
297 lua_setmetatable(L, 2);
298 lua_newtable(L); // 3: metatable for JSON objects and JSON arrays
299 lua_pushvalue(L, 3);
300 lua_setfield(L, 1, "metatable");
301 lua_pushvalue(L, 2);
302 lua_pushvalue(L, 3);
303 luaL_setfuncs(L, json_metatable_functions, 2);
304 luaL_setfuncs(L, json_module_functions, 2);
305 return 1;
306 }

Impressum / About Us