webmcp
annotate libraries/json/json.c @ 438:ea8419658535
Another change to make <db_object>:try_save() work properly with "document_column"
(use "_col" proxy also for accessing self._col[primary_key.json_doc])
(use "_col" proxy also for accessing self._col[primary_key.json_doc])
author | jbe |
---|---|
date | Wed Jan 20 21:06:07 2016 +0100 (2016-01-20) |
parents | 0bbf7717ebfd |
children | 839ff2b8d9f8 |
rev | line source |
---|---|
jbe@121 | 1 #include <lua.h> |
jbe@121 | 2 #include <lauxlib.h> |
jbe@122 | 3 #include <stdlib.h> |
jbe@121 | 4 #include <string.h> |
jbe@154 | 5 #include <math.h> |
jbe@432 | 6 #include <stdint.h> |
jbe@432 | 7 /* TODO: stdint.h only needed for Lua 5.2 compatibility */ |
jbe@121 | 8 |
jbe@144 | 9 // maximum number of nested JSON values (objects and arrays): |
jbe@182 | 10 // NOTE: json_import can store 2^32 / 3 levels on stack swap (using |
jbe@183 | 11 // also negative indicies after integer wraparound), and |
jbe@183 | 12 // json_export can store even more levels, so 1024^3 = |
jbe@183 | 13 // 1073741824 is a safe value and allows practically unlimited |
jbe@183 | 14 // levels for JSON documents <= 2 GiB. |
jbe@182 | 15 #define JSON_MAXDEPTH (1024*1024*1024) |
jbe@142 | 16 |
jbe@193 | 17 // define type JSON_LIGHTUSERDATA and |
jbe@193 | 18 // generate dummy memory addresses for lightuserdata values: |
jbe@193 | 19 #define JSON_LIGHTUSERDATA char |
jbe@193 | 20 static struct { |
jbe@193 | 21 JSON_LIGHTUSERDATA nullmark; // lightuserdata value represents a NULL value |
jbe@193 | 22 JSON_LIGHTUSERDATA shadowtbl; // lightuserdata key for shadow table |
jbe@193 | 23 } json_lightuserdata; |
jbe@193 | 24 |
jbe@193 | 25 // macros for special nullmark value: |
jbe@193 | 26 #define json_isnullmark(L, i) (lua_touserdata((L), (i)) == &json_lightuserdata.nullmark) |
jbe@193 | 27 #define json_pushnullmark(L) lua_pushlightuserdata((L), &json_lightuserdata.nullmark) |
jbe@193 | 28 |
jbe@193 | 29 // macros for getting and setting shadow tables |
jbe@193 | 30 #define json_setshadow(L, i) lua_rawsetp((L), (i), &json_lightuserdata.shadowtbl) |
jbe@193 | 31 #define json_getshadow(L, i) lua_rawgetp((L), (i), &json_lightuserdata.shadowtbl) |
jbe@193 | 32 #define json_createproxy(L) lua_createtable((L), 0, 1) |
jbe@193 | 33 |
jbe@193 | 34 // generate additional dummy memory addresses that represent Lua objects |
jbe@193 | 35 // via lightuserdata keys and LUA_REGISTRYINDEX: |
jbe@193 | 36 static struct { |
jbe@193 | 37 JSON_LIGHTUSERDATA objectmt; // metatable for JSON objects |
jbe@193 | 38 JSON_LIGHTUSERDATA arraymt; // metatable for JSON arrays |
jbe@193 | 39 } json_registry; |
jbe@155 | 40 |
jbe@144 | 41 // macros for usage of Lua registry: |
jbe@145 | 42 #define json_regpointer(x) (&json_registry.x) |
jbe@151 | 43 #define json_regfetchpointer(L, x) lua_rawgetp((L), LUA_REGISTRYINDEX, (x)) |
jbe@151 | 44 #define json_regfetch(L, x) json_regfetchpointer(L, json_regpointer(x)) |
jbe@151 | 45 #define json_regstore(L, x) lua_rawsetp(L, LUA_REGISTRYINDEX, json_regpointer(x)) |
jbe@145 | 46 |
jbe@157 | 47 // returns the string "<JSON null marker>": |
jbe@157 | 48 static int json_nullmark_tostring(lua_State *L) { |
jbe@157 | 49 lua_pushliteral(L, "<JSON null marker>"); |
jbe@157 | 50 return 1; |
jbe@157 | 51 } |
jbe@157 | 52 |
jbe@202 | 53 #define json_object_source_idx 1 |
jbe@202 | 54 #define json_object_iterator_idx 2 |
jbe@202 | 55 #define json_object_output_idx 3 |
jbe@202 | 56 #define json_object_shadow_idx 4 |
jbe@202 | 57 #define json_object_iterfun_idx 5 |
jbe@202 | 58 #define json_object_itertbl_idx 6 |
jbe@169 | 59 |
jbe@202 | 60 // converts a Lua table (or any other iterable value) to a JSON object: |
jbe@169 | 61 // (does never modify the argument, returns an empty object or array if argument is nil) |
jbe@202 | 62 static int json_object(lua_State *L) { |
jbe@171 | 63 // determine is argument is given: |
jbe@202 | 64 if (lua_isnoneornil(L, json_object_source_idx)) { |
jbe@171 | 65 // if no argument is given (or if argument is nil), |
jbe@193 | 66 // create proxy table with shadow table, and leave proxy table on top of stack: |
jbe@193 | 67 json_createproxy(L); |
jbe@169 | 68 lua_newtable(L); |
jbe@193 | 69 json_setshadow(L, -2); |
jbe@169 | 70 } else { |
jbe@171 | 71 // if an argument was given, |
jbe@193 | 72 // stack shall contain only one function argument: |
jbe@171 | 73 lua_settop(L, 1); |
jbe@193 | 74 // check if there is an iterator function in its metatable: |
jbe@202 | 75 if (luaL_getmetafield(L, json_object_source_idx, "__pairs")) { |
jbe@193 | 76 // if there is an iterator function, |
jbe@193 | 77 // leave it on stack position 2 and verify its type: |
jbe@202 | 78 if (lua_type(L, json_object_iterator_idx) != LUA_TFUNCTION) |
jbe@202 | 79 return luaL_error(L, "__pairs metamethod is not a function"); |
jbe@169 | 80 } else { |
jbe@193 | 81 // if there is no iterator function, |
jbe@193 | 82 // verify the type of the argument itself: |
jbe@202 | 83 luaL_checktype(L, json_object_source_idx, LUA_TTABLE); |
jbe@193 | 84 // push nil onto stack position 2: |
jbe@169 | 85 lua_pushnil(L); |
jbe@143 | 86 } |
jbe@171 | 87 // create result table on stack position 3: |
jbe@193 | 88 json_createproxy(L); |
jbe@169 | 89 // create shadow table on stack position 4: |
jbe@169 | 90 lua_newtable(L); |
jbe@193 | 91 lua_pushvalue(L, -1); |
jbe@193 | 92 json_setshadow(L, -3); |
jbe@171 | 93 // check if iterator function exists: |
jbe@202 | 94 if (lua_isnil(L, json_object_iterator_idx)) { |
jbe@171 | 95 // if there is no iterator function, |
jbe@202 | 96 // copy all string key value pairs to shadow table: |
jbe@202 | 97 for (lua_pushnil(L); lua_next(L, json_object_source_idx); lua_pop(L, 1)) { |
jbe@202 | 98 if (lua_type(L, -2) == LUA_TSTRING) { |
jbe@202 | 99 lua_pushvalue(L, -2); |
jbe@202 | 100 lua_pushvalue(L, -2); |
jbe@202 | 101 lua_rawset(L, json_object_shadow_idx); |
jbe@171 | 102 } |
jbe@169 | 103 } |
jbe@169 | 104 } else { |
jbe@172 | 105 // if there is an iterator function, |
jbe@172 | 106 // call iterator function with source value (first argument) |
jbe@172 | 107 // and store 3 result values on stack positions 5 through 7: |
jbe@202 | 108 lua_pushvalue(L, json_object_iterator_idx); |
jbe@172 | 109 lua_pushvalue(L, 1); |
jbe@172 | 110 lua_call(L, 1, 3); |
jbe@175 | 111 // iterate through key value pairs and store some of them in shadow table |
jbe@174 | 112 // while replacing nil values with null-marker: |
jbe@172 | 113 while (1) { |
jbe@175 | 114 // call iterfun function: |
jbe@202 | 115 lua_pushvalue(L, json_object_iterfun_idx); |
jbe@202 | 116 lua_pushvalue(L, json_object_itertbl_idx); |
jbe@172 | 117 lua_pushvalue(L, -3); |
jbe@172 | 118 lua_remove(L, -4); |
jbe@172 | 119 lua_call(L, 2, 2); |
jbe@175 | 120 // break iteration loop if key is nil: |
jbe@172 | 121 if (lua_isnil(L, -2)) break; |
jbe@175 | 122 // store key value pair only if key type is correct: |
jbe@202 | 123 if (lua_type(L, -2) == LUA_TSTRING) { |
jbe@175 | 124 // if key type is correct, |
jbe@175 | 125 // push key onto stack: |
jbe@172 | 126 lua_pushvalue(L, -2); |
jbe@175 | 127 // if value is nil, push null-marker onto stack (as value): |
jbe@174 | 128 if (lua_isnil(L, -2)) json_pushnullmark(L); |
jbe@175 | 129 // else push value onto stack: |
jbe@174 | 130 else lua_pushvalue(L, -2); |
jbe@175 | 131 // set key value pair in shadow table: |
jbe@202 | 132 lua_rawset(L, json_object_shadow_idx); |
jbe@172 | 133 } |
jbe@175 | 134 // pop value from stack, but leave key on stack: |
jbe@172 | 135 lua_pop(L, 1); |
jbe@172 | 136 } |
jbe@143 | 137 } |
jbe@171 | 138 // let result table be on top of stack: |
jbe@202 | 139 lua_settop(L, json_object_output_idx); |
jbe@136 | 140 } |
jbe@171 | 141 // set metatable (for result table on top of stack): |
jbe@202 | 142 json_regfetch(L, objectmt); |
jbe@169 | 143 lua_setmetatable(L, -2); |
jbe@171 | 144 // return table on top of stack: |
jbe@136 | 145 return 1; |
jbe@136 | 146 } |
jbe@136 | 147 |
jbe@202 | 148 #define json_array_source_idx 1 |
jbe@202 | 149 #define json_array_output_idx 2 |
jbe@202 | 150 #define json_array_shadow_idx 3 |
jbe@136 | 151 |
jbe@175 | 152 // converts a Lua table (or any other iterable value) to a JSON array: |
jbe@175 | 153 // (does never modify the argument, returns an empty object or array if argument is nil) |
jbe@136 | 154 static int json_array(lua_State *L) { |
jbe@202 | 155 // determine is argument is given: |
jbe@202 | 156 if (lua_isnoneornil(L, json_array_source_idx)) { |
jbe@202 | 157 // if no argument is given (or if argument is nil), |
jbe@202 | 158 // create proxy table with shadow table, and leave proxy table on top of stack: |
jbe@202 | 159 json_createproxy(L); |
jbe@202 | 160 lua_newtable(L); |
jbe@202 | 161 json_setshadow(L, -2); |
jbe@202 | 162 } else { |
jbe@202 | 163 lua_Integer arrayidx, arraylen; |
jbe@202 | 164 // if an argument was given, |
jbe@202 | 165 // stack shall contain only one function argument: |
jbe@202 | 166 lua_settop(L, 1); |
jbe@202 | 167 // create result table on stack position 2: |
jbe@202 | 168 json_createproxy(L); |
jbe@202 | 169 // create shadow table on stack position 3: |
jbe@202 | 170 lua_newtable(L); |
jbe@202 | 171 lua_pushvalue(L, -1); |
jbe@202 | 172 json_setshadow(L, -3); |
jbe@202 | 173 // determine length of array: |
jbe@202 | 174 arraylen = luaL_len(L, json_array_source_idx); |
jbe@202 | 175 // for an array, copy consecutive integer value pairs to shadow table: |
jbe@202 | 176 for (arrayidx=0; arrayidx<arraylen; ) { |
jbe@202 | 177 // increment arrayidx at head of loop: |
jbe@202 | 178 arrayidx++; |
jbe@202 | 179 // get next array entry: |
jbe@202 | 180 lua_pushinteger(L, arrayidx); |
jbe@202 | 181 lua_gettable(L, json_array_source_idx); |
jbe@202 | 182 // check if value is nil: |
jbe@202 | 183 if (lua_isnil(L, -1)) { |
jbe@202 | 184 // if yes, replace it with null-marker: |
jbe@202 | 185 lua_pop(L, 1); |
jbe@202 | 186 json_pushnullmark(L); |
jbe@202 | 187 } |
jbe@202 | 188 // store value in shadow table: |
jbe@202 | 189 lua_rawseti(L, json_array_shadow_idx, arrayidx); |
jbe@202 | 190 } |
jbe@202 | 191 // let result table be on top of stack: |
jbe@202 | 192 lua_settop(L, json_array_output_idx); |
jbe@202 | 193 } |
jbe@202 | 194 // set metatable (for result table on top of stack): |
jbe@202 | 195 json_regfetch(L, arraymt); |
jbe@202 | 196 lua_setmetatable(L, -2); |
jbe@202 | 197 // return table on top of stack: |
jbe@202 | 198 return 1; |
jbe@136 | 199 } |
jbe@136 | 200 |
jbe@145 | 201 // internal states of JSON parser: |
jbe@124 | 202 #define JSON_STATE_VALUE 0 |
jbe@124 | 203 #define JSON_STATE_OBJECT_KEY 1 |
jbe@124 | 204 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2 |
jbe@124 | 205 #define JSON_STATE_OBJECT_VALUE 3 |
jbe@124 | 206 #define JSON_STATE_OBJECT_SEPARATOR 4 |
jbe@124 | 207 #define JSON_STATE_ARRAY_VALUE 5 |
jbe@124 | 208 #define JSON_STATE_ARRAY_SEPARATOR 6 |
jbe@124 | 209 #define JSON_STATE_END 7 |
jbe@121 | 210 |
jbe@145 | 211 // special Lua stack indicies for json_import function: |
jbe@138 | 212 #define json_import_objectmt_idx 2 |
jbe@138 | 213 #define json_import_arraymt_idx 3 |
jbe@193 | 214 #define json_import_stackswap_idx 4 |
jbe@138 | 215 |
jbe@168 | 216 // macros for hex decoding: |
jbe@168 | 217 #define json_utf16_surrogate(x) ((x) >= 0xD800 && (x) <= 0xDFFF) |
jbe@168 | 218 #define json_utf16_lead(x) ((x) >= 0xD800 && (x) <= 0xDBFF) |
jbe@168 | 219 #define json_utf16_tail(x) ((x) >= 0xDC00 && (x) <= 0xDFFF) |
jbe@167 | 220 #define json_import_readhex(x) \ |
jbe@167 | 221 do { \ |
jbe@167 | 222 x = 0; \ |
jbe@167 | 223 for (i=0; i<4; i++) { \ |
jbe@167 | 224 x <<= 4; \ |
jbe@167 | 225 c = str[pos++]; \ |
jbe@167 | 226 if (c >= '0' && c <= '9') x += c - '0'; \ |
jbe@167 | 227 else if (c >= 'A' && c <= 'F') x += c - 'A' + 10; \ |
jbe@167 | 228 else if (c >= 'a' && c <= 'f') x += c - 'a' + 10; \ |
jbe@167 | 229 else if (c == 0) goto json_import_unexpected_eof; \ |
jbe@167 | 230 else goto json_import_unexpected_escape; \ |
jbe@167 | 231 } \ |
jbe@167 | 232 } while (0) |
jbe@167 | 233 |
jbe@136 | 234 // decodes a JSON document: |
jbe@121 | 235 static int json_import(lua_State *L) { |
jbe@181 | 236 int stackswapidx = 0; // elements in stack swap table |
jbe@181 | 237 int i; // loop variable |
jbe@181 | 238 const char *str; // string to parse |
jbe@181 | 239 size_t total; // total length of string to parse |
jbe@181 | 240 size_t pos = 0; // current position in string to parse |
jbe@181 | 241 size_t level = 0; // nested levels of objects/arrays currently being processed |
jbe@145 | 242 int mode = JSON_STATE_VALUE; // state of parser (i.e. "what's expected next?") |
jbe@181 | 243 unsigned char c; // variable to store a single character to be processed (unsigned!) |
jbe@181 | 244 luaL_Buffer luabuf; // Lua buffer to decode JSON string values |
jbe@181 | 245 char *cbuf; // C buffer to decode JSON string values |
jbe@181 | 246 size_t outlen; // maximum length or write position of C buffer |
jbe@181 | 247 long codepoint; // decoded UTF-16 character or higher codepoint |
jbe@181 | 248 long utf16tail; // second decoded UTF-16 character (surrogate tail) |
jbe@181 | 249 size_t arraylen; // variable to temporarily store the array length |
jbe@166 | 250 // require string as argument and convert to C string with length information: |
jbe@166 | 251 str = luaL_checklstring(L, 1, &total); |
jbe@166 | 252 // if string contains a NULL byte, this is a syntax error |
jbe@166 | 253 if (strlen(str) != total) goto json_import_syntax_error; |
jbe@147 | 254 // stack shall contain one function argument: |
jbe@138 | 255 lua_settop(L, 1); |
jbe@147 | 256 // push objectmt onto stack position 2: |
jbe@144 | 257 json_regfetch(L, objectmt); |
jbe@147 | 258 // push arraymt onto stack position 3: |
jbe@144 | 259 json_regfetch(L, arraymt); |
jbe@181 | 260 // push table for stack swapping onto stack position 5: |
jbe@181 | 261 // (needed to avoid Lua stack overflows) |
jbe@181 | 262 lua_newtable(L); |
jbe@136 | 263 // main loop of parser: |
jbe@136 | 264 json_import_loop: |
jbe@136 | 265 // skip whitespace and store next character in variable 'c': |
jbe@146 | 266 while (c = str[pos], |
jbe@146 | 267 c == ' ' || |
jbe@146 | 268 c == '\f' || |
jbe@146 | 269 c == '\n' || |
jbe@146 | 270 c == '\r' || |
jbe@146 | 271 c == '\t' || |
jbe@146 | 272 c == '\v' |
jbe@146 | 273 ) pos++; |
jbe@170 | 274 // NOTE: variable c needs to be unsigned in the following code |
jbe@136 | 275 // switch statement to handle certain (single) characters: |
jbe@121 | 276 switch (c) { |
jbe@136 | 277 // handle end of JSON document: |
jbe@121 | 278 case 0: |
jbe@136 | 279 // if end of JSON document was expected, then return top element of stack as result: |
jbe@124 | 280 if (mode == JSON_STATE_END) return 1; |
jbe@136 | 281 // otherwise, the JSON document was malformed: |
jbe@167 | 282 if (level == 0) { |
jbe@167 | 283 lua_pushnil(L); |
jbe@167 | 284 lua_pushliteral(L, "Empty string"); |
jbe@167 | 285 } else { |
jbe@167 | 286 json_import_unexpected_eof: |
jbe@167 | 287 lua_pushnil(L); |
jbe@167 | 288 lua_pushliteral(L, "Unexpected end of JSON document"); |
jbe@167 | 289 } |
jbe@121 | 290 return 2; |
jbe@181 | 291 // new JSON object or JSON array: |
jbe@121 | 292 case '{': |
jbe@181 | 293 case '[': |
jbe@181 | 294 // if an encountered JSON object is not expected here, then return an error: |
jbe@146 | 295 if ( |
jbe@181 | 296 c == '{' && |
jbe@181 | 297 mode != JSON_STATE_VALUE && |
jbe@181 | 298 mode != JSON_STATE_OBJECT_VALUE && |
jbe@181 | 299 mode != JSON_STATE_ARRAY_VALUE |
jbe@181 | 300 ) goto json_import_syntax_error; |
jbe@181 | 301 // if an encountered JSON array is not expected here, then return an error: |
jbe@181 | 302 if ( |
jbe@181 | 303 c == '[' && |
jbe@146 | 304 mode != JSON_STATE_VALUE && |
jbe@146 | 305 mode != JSON_STATE_OBJECT_VALUE && |
jbe@146 | 306 mode != JSON_STATE_ARRAY_VALUE |
jbe@146 | 307 ) goto json_import_syntax_error; |
jbe@181 | 308 // consume input character: |
jbe@181 | 309 pos++; |
jbe@181 | 310 // limit nested levels: |
jbe@181 | 311 if (level >= JSON_MAXDEPTH) { |
jbe@181 | 312 lua_pushnil(L); |
jbe@181 | 313 lua_pushfstring(L, "More than %d nested JSON levels", JSON_MAXDEPTH); |
jbe@181 | 314 return 2; |
jbe@181 | 315 } |
jbe@181 | 316 // swap Lua stack entries for previous level to swap table: |
jbe@181 | 317 // (avoids depth limitations due to Lua stack size) |
jbe@181 | 318 if (level) { |
jbe@181 | 319 lua_rawseti(L, json_import_stackswap_idx, ++stackswapidx); |
jbe@181 | 320 lua_rawseti(L, json_import_stackswap_idx, ++stackswapidx); |
jbe@181 | 321 lua_rawseti(L, json_import_stackswap_idx, ++stackswapidx); |
jbe@181 | 322 } |
jbe@181 | 323 // increment level: |
jbe@181 | 324 level++; |
jbe@181 | 325 // create JSON object or JSON array on stack: |
jbe@136 | 326 lua_newtable(L); |
jbe@181 | 327 // set metatable of JSON object or JSON array: |
jbe@181 | 328 lua_pushvalue(L, c == '{' ? json_import_objectmt_idx : json_import_arraymt_idx); |
jbe@125 | 329 lua_setmetatable(L, -2); |
jbe@136 | 330 // create internal shadow table on stack: |
jbe@136 | 331 lua_newtable(L); |
jbe@146 | 332 // register internal shadow table: |
jbe@193 | 333 lua_pushvalue(L, -1); |
jbe@193 | 334 json_setshadow(L, -3); |
jbe@181 | 335 // distinguish between JSON objects and JSON arrays: |
jbe@181 | 336 if (c == '{') { |
jbe@181 | 337 // if JSON object, |
jbe@181 | 338 // expect object key (or end of object) to follow: |
jbe@181 | 339 mode = JSON_STATE_OBJECT_KEY; |
jbe@181 | 340 } else { |
jbe@181 | 341 // if JSON array, |
jbe@181 | 342 // expect array value (or end of array) to follow: |
jbe@181 | 343 mode = JSON_STATE_ARRAY_VALUE; |
jbe@181 | 344 // add nil as key (needed to keep stack balance) and as magic to detect arrays: |
jbe@181 | 345 if (c == '[') lua_pushnil(L); |
jbe@142 | 346 } |
jbe@121 | 347 goto json_import_loop; |
jbe@136 | 348 // end of JSON object: |
jbe@121 | 349 case '}': |
jbe@136 | 350 // if end of JSON object is not expected here, then return an error: |
jbe@146 | 351 if ( |
jbe@146 | 352 mode != JSON_STATE_OBJECT_KEY && |
jbe@146 | 353 mode != JSON_STATE_OBJECT_SEPARATOR |
jbe@146 | 354 ) goto json_import_syntax_error; |
jbe@136 | 355 // jump to common code for end of JSON object and JSON array: |
jbe@121 | 356 goto json_import_close; |
jbe@136 | 357 // end of JSON array: |
jbe@121 | 358 case ']': |
jbe@136 | 359 // if end of JSON array is not expected here, then return an error: |
jbe@146 | 360 if ( |
jbe@146 | 361 mode != JSON_STATE_ARRAY_VALUE && |
jbe@146 | 362 mode != JSON_STATE_ARRAY_SEPARATOR |
jbe@146 | 363 ) goto json_import_syntax_error; |
jbe@146 | 364 // pop nil key/magic (that was needed to keep stack balance): |
jbe@140 | 365 lua_pop(L, 1); |
jbe@136 | 366 // continue with common code for end of JSON object and JSON array: |
jbe@136 | 367 // common code for end of JSON object or JSON array: |
jbe@121 | 368 json_import_close: |
jbe@136 | 369 // consume input character: |
jbe@121 | 370 pos++; |
jbe@136 | 371 // pop shadow table: |
jbe@136 | 372 lua_pop(L, 1); |
jbe@136 | 373 // check if nested: |
jbe@121 | 374 if (--level) { |
jbe@146 | 375 // if nested, |
jbe@181 | 376 // restore previous stack elements from stack swap: |
jbe@181 | 377 lua_rawgeti(L, json_import_stackswap_idx, stackswapidx--); |
jbe@181 | 378 lua_insert(L, -2); |
jbe@181 | 379 lua_rawgeti(L, json_import_stackswap_idx, stackswapidx--); |
jbe@181 | 380 lua_insert(L, -2); |
jbe@181 | 381 lua_rawgeti(L, json_import_stackswap_idx, stackswapidx--); |
jbe@181 | 382 lua_insert(L, -2); |
jbe@146 | 383 // check if outer(!) structure is an array or object: |
jbe@140 | 384 if (lua_isnil(L, -2)) { |
jbe@136 | 385 // select array value processing: |
jbe@124 | 386 mode = JSON_STATE_ARRAY_VALUE; |
jbe@121 | 387 } else { |
jbe@136 | 388 // select object value processing: |
jbe@124 | 389 mode = JSON_STATE_OBJECT_VALUE; |
jbe@121 | 390 } |
jbe@136 | 391 // store value in outer structure: |
jbe@121 | 392 goto json_import_process_value; |
jbe@121 | 393 } |
jbe@136 | 394 // if not nested, then expect end of JSON document and continue with loop: |
jbe@136 | 395 mode = JSON_STATE_END; |
jbe@121 | 396 goto json_import_loop; |
jbe@136 | 397 // key terminator: |
jbe@121 | 398 case ':': |
jbe@136 | 399 // if key terminator is not expected here, then return an error: |
jbe@124 | 400 if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR) |
jbe@121 | 401 goto json_import_syntax_error; |
jbe@136 | 402 // consume input character: |
jbe@121 | 403 pos++; |
jbe@146 | 404 // expect object value to follow: |
jbe@124 | 405 mode = JSON_STATE_OBJECT_VALUE; |
jbe@146 | 406 // continue with loop: |
jbe@121 | 407 goto json_import_loop; |
jbe@136 | 408 // value terminator (NOTE: trailing comma at end of value or key-value list is tolerated by this parser) |
jbe@121 | 409 case ',': |
jbe@146 | 410 // branch according to parser state: |
jbe@124 | 411 if (mode == JSON_STATE_OBJECT_SEPARATOR) { |
jbe@146 | 412 // expect an object key to follow: |
jbe@124 | 413 mode = JSON_STATE_OBJECT_KEY; |
jbe@124 | 414 } else if (mode == JSON_STATE_ARRAY_SEPARATOR) { |
jbe@146 | 415 // expect an array value to follow: |
jbe@124 | 416 mode = JSON_STATE_ARRAY_VALUE; |
jbe@121 | 417 } else { |
jbe@136 | 418 // if value terminator is not expected here, then return an error: |
jbe@136 | 419 goto json_import_syntax_error; |
jbe@121 | 420 } |
jbe@136 | 421 // consume input character: |
jbe@121 | 422 pos++; |
jbe@136 | 423 // continue with loop: |
jbe@121 | 424 goto json_import_loop; |
jbe@136 | 425 // string literal: |
jbe@121 | 426 case '"': |
jbe@146 | 427 // consume quote character: |
jbe@146 | 428 pos++; |
jbe@162 | 429 // find last character in input string: |
jbe@162 | 430 outlen = pos; |
jbe@162 | 431 while ((c = str[outlen]) != '"') { |
jbe@161 | 432 // consume one character: |
jbe@162 | 433 outlen++; |
jbe@161 | 434 // handle unexpected end of JSON document: |
jbe@161 | 435 if (c == 0) goto json_import_unexpected_eof; |
jbe@161 | 436 // consume one extra character when encountering an escaped quote: |
jbe@162 | 437 else if (c == '\\' && str[outlen] == '"') outlen++; |
jbe@161 | 438 } |
jbe@162 | 439 // determine buffer length: |
jbe@162 | 440 outlen -= pos; |
jbe@161 | 441 // check if string is non empty: |
jbe@162 | 442 if (outlen) { |
jbe@161 | 443 // prepare buffer to decode string (with maximum possible length) and set write position to zero: |
jbe@162 | 444 cbuf = luaL_buffinitsize(L, &luabuf, outlen); |
jbe@162 | 445 outlen = 0; |
jbe@161 | 446 // loop through the characters until encountering end quote: |
jbe@161 | 447 while ((c = str[pos++]) != '"') { |
jbe@162 | 448 // NOTE: unexpected end cannot happen anymore |
jbe@162 | 449 if (c < 32 || c == 127) { |
jbe@161 | 450 // do not allow ASCII control characters: |
jbe@161 | 451 // NOTE: illegal UTF-8 sequences and extended control characters are not sanitized |
jbe@161 | 452 // by this parser to allow different encodings than Unicode |
jbe@161 | 453 lua_pushnil(L); |
jbe@161 | 454 lua_pushliteral(L, "Unexpected control character in JSON string"); |
jbe@161 | 455 return 2; |
jbe@161 | 456 } else if (c == '\\') { |
jbe@161 | 457 // read next char after backslash escape: |
jbe@161 | 458 c = str[pos++]; |
jbe@161 | 459 switch (c) { |
jbe@161 | 460 // unexpected end-of-string: |
jbe@161 | 461 case 0: |
jbe@161 | 462 goto json_import_unexpected_eof; |
jbe@161 | 463 // unescaping of quotation mark, slash, and backslash: |
jbe@161 | 464 case '"': |
jbe@161 | 465 case '/': |
jbe@161 | 466 case '\\': |
jbe@162 | 467 cbuf[outlen++] = c; |
jbe@161 | 468 break; |
jbe@161 | 469 // unescaping of backspace: |
jbe@162 | 470 case 'b': cbuf[outlen++] = '\b'; break; |
jbe@161 | 471 // unescaping of form-feed: |
jbe@162 | 472 case 'f': cbuf[outlen++] = '\f'; break; |
jbe@161 | 473 // unescaping of new-line: |
jbe@162 | 474 case 'n': cbuf[outlen++] = '\n'; break; |
jbe@161 | 475 // unescaping of carriage-return: |
jbe@162 | 476 case 'r': cbuf[outlen++] = '\r'; break; |
jbe@161 | 477 // unescaping of tabulator: |
jbe@162 | 478 case 't': cbuf[outlen++] = '\t'; break; |
jbe@161 | 479 // unescaping of UTF-16 characters |
jbe@161 | 480 case 'u': |
jbe@167 | 481 // decode 4 hex nibbles: |
jbe@167 | 482 json_import_readhex(codepoint); |
jbe@167 | 483 // handle surrogate character: |
jbe@167 | 484 if (json_utf16_surrogate(codepoint)) { |
jbe@167 | 485 // check if first surrogate is in valid range: |
jbe@167 | 486 if (json_utf16_lead(codepoint)) { |
jbe@167 | 487 // require second surrogate: |
jbe@167 | 488 if ((c = str[pos++]) != '\\' || (c = str[pos++]) != 'u') { |
jbe@167 | 489 if (c == 0) goto json_import_unexpected_eof; |
jbe@167 | 490 else goto json_import_wrong_surrogate; |
jbe@167 | 491 } |
jbe@167 | 492 // read 4 hex nibbles of second surrogate character: |
jbe@167 | 493 json_import_readhex(utf16tail); |
jbe@167 | 494 // check if second surrogate is in valid range: |
jbe@167 | 495 if (!json_utf16_tail(utf16tail)) goto json_import_wrong_surrogate; |
jbe@167 | 496 // calculate codepoint: |
jbe@167 | 497 codepoint = 0x10000 + (utf16tail - 0xDC00) + (codepoint - 0xD800) * 0x400; |
jbe@167 | 498 } else { |
jbe@167 | 499 // throw error for wrong surrogates: |
jbe@167 | 500 json_import_wrong_surrogate: |
jbe@167 | 501 lua_pushnil(L); |
jbe@167 | 502 lua_pushliteral(L, "Illegal UTF-16 surrogate in JSON string escape sequence"); |
jbe@167 | 503 return 2; |
jbe@167 | 504 } |
jbe@167 | 505 } |
jbe@167 | 506 // encode as UTF-8: |
jbe@167 | 507 if (codepoint < 0x80) { |
jbe@167 | 508 cbuf[outlen++] = (char)codepoint; |
jbe@167 | 509 } else if (codepoint < 0x800) { |
jbe@167 | 510 cbuf[outlen++] = (char)(0xc0 | (codepoint >> 6)); |
jbe@167 | 511 cbuf[outlen++] = (char)(0x80 | (codepoint & 0x3f)); |
jbe@167 | 512 } else if (codepoint < 0x10000) { |
jbe@167 | 513 cbuf[outlen++] = (char)(0xe0 | (codepoint >> 12)); |
jbe@167 | 514 cbuf[outlen++] = (char)(0x80 | ((codepoint >> 6) & 0x3f)); |
jbe@167 | 515 cbuf[outlen++] = (char)(0x80 | (codepoint & 0x3f)); |
jbe@167 | 516 } else { |
jbe@167 | 517 cbuf[outlen++] = (char)(0xf0 | (codepoint >> 18)); |
jbe@167 | 518 cbuf[outlen++] = (char)(0x80 | ((codepoint >> 12) & 0x3f)); |
jbe@167 | 519 cbuf[outlen++] = (char)(0x80 | ((codepoint >> 6) & 0x3f)); |
jbe@167 | 520 cbuf[outlen++] = (char)(0x80 | (codepoint & 0x3f)); |
jbe@167 | 521 } |
jbe@167 | 522 break; |
jbe@161 | 523 // unexpected escape sequence: |
jbe@161 | 524 default: |
jbe@167 | 525 json_import_unexpected_escape: |
jbe@161 | 526 lua_pushnil(L); |
jbe@161 | 527 lua_pushliteral(L, "Unexpected string escape sequence in JSON document"); |
jbe@161 | 528 return 2; |
jbe@161 | 529 } |
jbe@161 | 530 } else { |
jbe@161 | 531 // normal character: |
jbe@162 | 532 cbuf[outlen++] = c; |
jbe@121 | 533 } |
jbe@121 | 534 } |
jbe@161 | 535 // process buffer to Lua string: |
jbe@162 | 536 luaL_pushresultsize(&luabuf, outlen); |
jbe@161 | 537 } else { |
jbe@161 | 538 // if JSON string is empty, |
jbe@161 | 539 // push empty Lua string: |
jbe@161 | 540 lua_pushliteral(L, ""); |
jbe@167 | 541 // consume closing quote: |
jbe@167 | 542 pos++; |
jbe@121 | 543 } |
jbe@136 | 544 // continue with processing of decoded string: |
jbe@121 | 545 goto json_import_process_value; |
jbe@121 | 546 } |
jbe@136 | 547 // process values whose type is is not deducible from a single character: |
jbe@136 | 548 if ((c >= '0' && c <= '9') || c == '-' || c == '+') { |
jbe@427 | 549 // try to parse number: |
jbe@167 | 550 double numval; |
jbe@122 | 551 char *endptr; |
jbe@427 | 552 size_t endpos; |
jbe@427 | 553 // use strtod() call to parse a (double precision) floating point number |
jbe@427 | 554 // and to determine length of number: |
jbe@122 | 555 numval = strtod(str+pos, &endptr); |
jbe@146 | 556 // catch parsing errors: |
jbe@122 | 557 if (endptr == str+pos) goto json_import_syntax_error; |
jbe@427 | 558 // calculate end position of number: |
jbe@427 | 559 endpos = endptr - str; |
jbe@427 | 560 #if LUA_VERSION_NUM >= 503 |
jbe@427 | 561 // try alternative integer interpretation: |
jbe@427 | 562 { |
jbe@427 | 563 lua_Integer intval = 0; |
jbe@427 | 564 size_t curpos; |
jbe@427 | 565 if (c >= '0' && c <= '9') intval = c - '0'; |
jbe@427 | 566 for (curpos=pos+1; curpos<endpos; curpos++) { |
jbe@427 | 567 lua_Integer d = str[curpos] - '0'; |
jbe@427 | 568 if (d < 0 || d > 9) break; |
jbe@427 | 569 if (c == '-') { |
jbe@427 | 570 // NOTE: rounding of negative integer division may be undefined |
jbe@427 | 571 if ( |
jbe@427 | 572 intval == LUA_MININTEGER || |
jbe@427 | 573 -intval > (-(LUA_MININTEGER+10) - d) / 10 + 1 |
jbe@427 | 574 ) break; |
jbe@427 | 575 intval = 10 * intval - d; |
jbe@427 | 576 } else { |
jbe@427 | 577 if (intval > (LUA_MAXINTEGER - d) / 10) break; |
jbe@427 | 578 intval = 10 * intval + d; |
jbe@427 | 579 } |
jbe@427 | 580 } |
jbe@427 | 581 if (curpos == endpos) lua_pushinteger(L, intval); |
jbe@427 | 582 else lua_pushnumber(L, numval); |
jbe@427 | 583 } |
jbe@427 | 584 #endif |
jbe@146 | 585 // consume characters that were parsed: |
jbe@427 | 586 pos = endpos; |
jbe@122 | 587 } else if (!strncmp(str+pos, "true", 4)) { |
jbe@136 | 588 // consume 4 input characters for "true": |
jbe@121 | 589 pos += 4; |
jbe@147 | 590 // put Lua true value onto stack: |
jbe@136 | 591 lua_pushboolean(L, 1); |
jbe@121 | 592 } else if (!strncmp(str+pos, "false", 5)) { |
jbe@136 | 593 // consume 5 input characters for "false": |
jbe@121 | 594 pos += 5; |
jbe@147 | 595 // put Lua false value onto stack: |
jbe@136 | 596 lua_pushboolean(L, 0); |
jbe@121 | 597 } else if (!strncmp(str+pos, "null", 4)) { |
jbe@136 | 598 // consume 4 input characters for "null": |
jbe@136 | 599 pos += 4; |
jbe@194 | 600 // push special null-marker onto stack: |
jbe@194 | 601 json_pushnullmark(L); |
jbe@121 | 602 } else { |
jbe@136 | 603 // all other cases are a syntax error: |
jbe@121 | 604 goto json_import_syntax_error; |
jbe@121 | 605 } |
jbe@136 | 606 // process a decoded value or key value pair (expected on top of Lua stack): |
jbe@136 | 607 json_import_process_value: |
jbe@121 | 608 switch (mode) { |
jbe@136 | 609 // an object key has been read: |
jbe@124 | 610 case JSON_STATE_OBJECT_KEY: |
jbe@136 | 611 // if an object key is not a string, then this is a syntax error: |
jbe@121 | 612 if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error; |
jbe@146 | 613 // expect key terminator to follow: |
jbe@124 | 614 mode = JSON_STATE_OBJECT_KEY_TERMINATOR; |
jbe@146 | 615 // continue with loop: |
jbe@121 | 616 goto json_import_loop; |
jbe@136 | 617 // a key value pair has been read: |
jbe@124 | 618 case JSON_STATE_OBJECT_VALUE: |
jbe@136 | 619 // store key value pair in outer shadow table: |
jbe@130 | 620 lua_rawset(L, -3); |
jbe@146 | 621 // expect value terminator (or end of object) to follow: |
jbe@124 | 622 mode = JSON_STATE_OBJECT_SEPARATOR; |
jbe@146 | 623 // continue with loop: |
jbe@121 | 624 goto json_import_loop; |
jbe@136 | 625 // an array value has been read: |
jbe@124 | 626 case JSON_STATE_ARRAY_VALUE: |
jbe@152 | 627 // get current array length: |
jbe@152 | 628 arraylen = lua_rawlen(L, -3); |
jbe@427 | 629 // throw error if array would exceed INT_MAX-1 elements: |
jbe@427 | 630 // NOTE: Lua 5.3 may support more elements, but C libraries may not |
jbe@427 | 631 if (arraylen > INT_MAX-1) { |
jbe@152 | 632 lua_pushnil(L); |
jbe@427 | 633 lua_pushfstring(L, "Array exceeded length of %d elements", INT_MAX-1); |
jbe@152 | 634 } |
jbe@136 | 635 // store value in outer shadow table: |
jbe@152 | 636 lua_rawseti(L, -3, arraylen + 1); |
jbe@146 | 637 // expect value terminator (or end of object) to follow: |
jbe@124 | 638 mode = JSON_STATE_ARRAY_SEPARATOR; |
jbe@146 | 639 // continue with loop |
jbe@121 | 640 goto json_import_loop; |
jbe@136 | 641 // a single value has been read: |
jbe@124 | 642 case JSON_STATE_VALUE: |
jbe@136 | 643 // leave value on top of stack, expect end of JSON document, and continue with loop: |
jbe@124 | 644 mode = JSON_STATE_END; |
jbe@121 | 645 goto json_import_loop; |
jbe@121 | 646 } |
jbe@146 | 647 // syntax error handling (reachable by goto statement): |
jbe@136 | 648 json_import_syntax_error: |
jbe@121 | 649 lua_pushnil(L); |
jbe@121 | 650 lua_pushliteral(L, "Syntax error in JSON document"); |
jbe@121 | 651 return 2; |
jbe@121 | 652 } |
jbe@121 | 653 |
jbe@146 | 654 // gets a value or its type from a JSON document (passed as first argument) |
jbe@175 | 655 // using a path (passed as variable number of keys after the first argument): |
jbe@137 | 656 static int json_path(lua_State *L, int type_mode) { |
jbe@193 | 657 int stacktop; // number of arguments |
jbe@193 | 658 int idx = 2; // stack index of current argument to process |
jbe@173 | 659 // require at least one argument: |
jbe@173 | 660 luaL_checkany(L, 1); |
jbe@193 | 661 // store stack index of top of stack (number of arguments): |
jbe@138 | 662 stacktop = lua_gettop(L); |
jbe@146 | 663 // use first argument as "current value" (stored on top of stack): |
jbe@193 | 664 lua_pushvalue(L, 1); |
jbe@146 | 665 // process each "path key" (2nd argument and following arguments): |
jbe@138 | 666 while (idx <= stacktop) { |
jbe@146 | 667 // if "current value" (on top of stack) is nil, then the path cannot be walked and nil is returned: |
jbe@137 | 668 if (lua_isnil(L, -1)) return 1; |
jbe@137 | 669 // try to get shadow table of "current value": |
jbe@193 | 670 json_getshadow(L, -1); |
jbe@126 | 671 if (lua_isnil(L, -1)) { |
jbe@137 | 672 // if no shadow table is found, |
jbe@193 | 673 if (lua_type(L, -2) == LUA_TTABLE) { |
jbe@146 | 674 // and if "current value" is a table, |
jbe@193 | 675 // pop nil from stack: |
jbe@146 | 676 lua_pop(L, 1); |
jbe@137 | 677 // get "next value" using the "path key": |
jbe@130 | 678 lua_pushvalue(L, idx++); |
jbe@130 | 679 lua_gettable(L, -2); |
jbe@130 | 680 } else { |
jbe@137 | 681 // if "current value" is not a table, |
jbe@146 | 682 // then the path cannot be walked and nil (already on top of stack) is returned: |
jbe@137 | 683 return 1; |
jbe@130 | 684 } |
jbe@130 | 685 } else { |
jbe@137 | 686 // if a shadow table is found, |
jbe@137 | 687 // set "current value" to its shadow table: |
jbe@130 | 688 lua_replace(L, -2); |
jbe@137 | 689 // get "next value" using the "path key": |
jbe@130 | 690 lua_pushvalue(L, idx++); |
jbe@130 | 691 lua_rawget(L, -2); |
jbe@126 | 692 } |
jbe@137 | 693 // the "next value" replaces the "current value": |
jbe@130 | 694 lua_replace(L, -2); |
jbe@126 | 695 } |
jbe@137 | 696 if (!type_mode) { |
jbe@137 | 697 // if a value (and not its type) was requested, |
jbe@137 | 698 // check if value is the null-marker, and store nil on top of Lua stack in that case: |
jbe@155 | 699 if (json_isnullmark(L, -1)) lua_pushnil(L); |
jbe@137 | 700 } else { |
jbe@137 | 701 // if the type was requested, |
jbe@137 | 702 // check if value is the null-marker: |
jbe@155 | 703 if (json_isnullmark(L, -1)) { |
jbe@137 | 704 // if yes, store string "null" on top of Lua stack: |
jbe@130 | 705 lua_pushliteral(L, "null"); |
jbe@137 | 706 } else { |
jbe@137 | 707 // otherwise, |
jbe@138 | 708 // check if metatable indicates "object" or "array": |
jbe@138 | 709 if (lua_getmetatable(L, -1)) { |
jbe@144 | 710 json_regfetch(L, objectmt); |
jbe@138 | 711 if (lua_rawequal(L, -2, -1)) { |
jbe@146 | 712 // if value has metatable for JSON objects, |
jbe@138 | 713 // return string "object": |
jbe@138 | 714 lua_pushliteral(L, "object"); |
jbe@138 | 715 return 1; |
jbe@138 | 716 } |
jbe@144 | 717 json_regfetch(L, arraymt); |
jbe@138 | 718 if (lua_rawequal(L, -3, -1)) { |
jbe@146 | 719 // if value has metatable for JSON arrays, |
jbe@146 | 720 // return string "object": |
jbe@138 | 721 lua_pushliteral(L, "array"); |
jbe@138 | 722 return 1; |
jbe@138 | 723 } |
jbe@146 | 724 // remove 3 metatables (one of the value, two for comparison) from stack: |
jbe@138 | 725 lua_pop(L, 3); |
jbe@138 | 726 } |
jbe@138 | 727 // otherwise, get the Lua type: |
jbe@138 | 728 lua_pushstring(L, lua_typename(L, lua_type(L, -1))); |
jbe@126 | 729 } |
jbe@126 | 730 } |
jbe@137 | 731 // return the top most value on the Lua stack: |
jbe@137 | 732 return 1; |
jbe@130 | 733 } |
jbe@130 | 734 |
jbe@147 | 735 // gets a value from a JSON document (passed as first argument) |
jbe@175 | 736 // using a path (passed as variable number of keys after the first argument): |
jbe@130 | 737 static int json_get(lua_State *L) { |
jbe@137 | 738 return json_path(L, 0); |
jbe@130 | 739 } |
jbe@130 | 740 |
jbe@147 | 741 // gets a value's type from a JSON document (passed as first argument) |
jbe@175 | 742 // using a path (passed as variable number of keys after first the argument): |
jbe@130 | 743 static int json_type(lua_State *L) { |
jbe@137 | 744 return json_path(L, 1); |
jbe@130 | 745 } |
jbe@130 | 746 |
jbe@173 | 747 // special Lua stack indicies for json_set function: |
jbe@193 | 748 #define json_set_objectmt_idx 1 |
jbe@193 | 749 #define json_set_arraymt_idx 2 |
jbe@173 | 750 |
jbe@173 | 751 // stack offset of arguments to json_set function: |
jbe@193 | 752 #define json_set_idxshift 2 |
jbe@173 | 753 |
jbe@173 | 754 // sets a value (passed as second argument) in a JSON document (passed as first argument) |
jbe@175 | 755 // using a path (passed as variable number of keys starting at third argument): |
jbe@173 | 756 static int json_set(lua_State *L) { |
jbe@193 | 757 int stacktop; // stack index of top of stack (after shifting) |
jbe@193 | 758 int idx; // stack index of current argument to process |
jbe@199 | 759 // require at least three arguments: |
jbe@173 | 760 luaL_checkany(L, 1); |
jbe@173 | 761 luaL_checkany(L, 2); |
jbe@199 | 762 luaL_checkany(L, 3); |
jbe@193 | 763 // insert objectmt into stack at position 1 (shifting the arguments): |
jbe@173 | 764 json_regfetch(L, objectmt); |
jbe@193 | 765 lua_insert(L, 1); |
jbe@193 | 766 // insert arraymt into stack at position 2 (shifting the arguments): |
jbe@193 | 767 json_regfetch(L, arraymt); |
jbe@173 | 768 lua_insert(L, 2); |
jbe@173 | 769 // store stack index of top of stack: |
jbe@173 | 770 stacktop = lua_gettop(L); |
jbe@173 | 771 // use nil as initial "parent value": |
jbe@173 | 772 lua_pushnil(L); |
jbe@173 | 773 // use first argument as "current value": |
jbe@173 | 774 lua_pushvalue(L, 1 + json_set_idxshift); |
jbe@173 | 775 // set all necessary values in path: |
jbe@173 | 776 for (idx = 3 + json_set_idxshift; idx<=stacktop; idx++) { |
jbe@173 | 777 // push metatable of "current value" onto stack: |
jbe@173 | 778 if (!lua_getmetatable(L, -1)) lua_pushnil(L); |
jbe@173 | 779 // distinguish according to type of path key: |
jbe@173 | 780 switch (lua_type(L, idx)) { |
jbe@173 | 781 case LUA_TSTRING: |
jbe@173 | 782 // if path key is a string, |
jbe@173 | 783 // check if "current value" is a JSON object (or table without metatable): |
jbe@173 | 784 if ( |
jbe@173 | 785 lua_rawequal(L, -1, json_set_objectmt_idx) || |
jbe@173 | 786 (lua_isnil(L, -1) && lua_type(L, -2) == LUA_TTABLE) |
jbe@173 | 787 ) { |
jbe@173 | 788 // if "current value" is acceptable, |
jbe@173 | 789 // pop metatable and leave "current value" on top of stack: |
jbe@173 | 790 lua_pop(L, 1); |
jbe@173 | 791 } else { |
jbe@173 | 792 // if "current value" is not acceptable: |
jbe@173 | 793 // pop metatable and "current value": |
jbe@173 | 794 lua_pop(L, 2); |
jbe@173 | 795 // throw error if parent element does not exist: |
jbe@173 | 796 if (lua_isnil(L, -1)) return luaL_error(L, "Root element is not a JSON object"); |
jbe@173 | 797 // push new JSON object as "current value" onto stack: |
jbe@193 | 798 json_createproxy(L); |
jbe@173 | 799 // create and register shadow table: |
jbe@173 | 800 lua_newtable(L); |
jbe@193 | 801 json_setshadow(L, -2); |
jbe@173 | 802 // set metatable of JSON object: |
jbe@173 | 803 lua_pushvalue(L, json_set_objectmt_idx); |
jbe@173 | 804 lua_setmetatable(L, -2); |
jbe@173 | 805 // set entry in "parent value": |
jbe@173 | 806 lua_pushvalue(L, idx-1); |
jbe@173 | 807 lua_pushvalue(L, -2); |
jbe@173 | 808 lua_settable(L, -4); |
jbe@173 | 809 } |
jbe@173 | 810 break; |
jbe@173 | 811 case LUA_TNUMBER: |
jbe@173 | 812 // if path key is a number, |
jbe@173 | 813 // check if "current value" is a JSON array (or table without metatable): |
jbe@173 | 814 if ( |
jbe@173 | 815 lua_rawequal(L, -1, json_set_arraymt_idx) || |
jbe@173 | 816 (lua_isnil(L, -1) && lua_type(L, -2) == LUA_TTABLE) |
jbe@173 | 817 ) { |
jbe@173 | 818 // if "current value" is acceptable, |
jbe@173 | 819 // pop metatable and leave "current value" on top of stack: |
jbe@173 | 820 lua_pop(L, 1); |
jbe@173 | 821 } else { |
jbe@173 | 822 // if "current value" is not acceptable: |
jbe@173 | 823 // pop metatable and "current value": |
jbe@173 | 824 lua_pop(L, 2); |
jbe@173 | 825 // throw error if parent element does not exist: |
jbe@173 | 826 if (lua_isnil(L, -1)) return luaL_error(L, "Root element is not a JSON array"); |
jbe@173 | 827 // push new JSON array as "current value" onto stack: |
jbe@193 | 828 json_createproxy(L); |
jbe@173 | 829 // create and register shadow table: |
jbe@173 | 830 lua_newtable(L); |
jbe@193 | 831 json_setshadow(L, -2); |
jbe@173 | 832 // set metatable of JSON array: |
jbe@173 | 833 lua_pushvalue(L, json_set_arraymt_idx); |
jbe@173 | 834 lua_setmetatable(L, -2); |
jbe@173 | 835 // set entry in "parent value": |
jbe@173 | 836 lua_pushvalue(L, idx-1); |
jbe@173 | 837 lua_pushvalue(L, -2); |
jbe@173 | 838 lua_settable(L, -4); |
jbe@173 | 839 } |
jbe@173 | 840 break; |
jbe@173 | 841 default: |
jbe@173 | 842 return luaL_error(L, "Invalid path key of type %s", lua_typename(L, lua_type(L, idx))); |
jbe@173 | 843 } |
jbe@173 | 844 // check if last path element is being processed: |
jbe@173 | 845 if (idx == stacktop) { |
jbe@173 | 846 // if the last path element is being processed, |
jbe@173 | 847 // set last path value in "current value" container: |
jbe@173 | 848 lua_pushvalue(L, idx); |
jbe@173 | 849 lua_pushvalue(L, 2 + json_set_idxshift); |
jbe@173 | 850 lua_settable(L, -3); |
jbe@173 | 851 } else { |
jbe@173 | 852 // if the processed path element is not the last, |
jbe@173 | 853 // use old "current value" as new "parent value" |
jbe@173 | 854 lua_remove(L, -2); |
jbe@173 | 855 // push new "current value" onto stack by performing a lookup: |
jbe@173 | 856 lua_pushvalue(L, idx); |
jbe@173 | 857 lua_gettable(L, -2); |
jbe@173 | 858 } |
jbe@173 | 859 } |
jbe@173 | 860 // return first argument for convenience: |
jbe@173 | 861 lua_settop(L, 1 + json_set_idxshift); |
jbe@173 | 862 return 1; |
jbe@173 | 863 } |
jbe@173 | 864 |
jbe@147 | 865 // returns the length of a JSON array (or zero for a table without numeric keys): |
jbe@130 | 866 static int json_len(lua_State *L) { |
jbe@201 | 867 // require table as first argument: |
jbe@201 | 868 luaL_checktype(L, 1, LUA_TTABLE); |
jbe@147 | 869 // stack shall contain one function argument: |
jbe@130 | 870 lua_settop(L, 1); |
jbe@193 | 871 // push shadow table or nil onto stack: |
jbe@193 | 872 json_getshadow(L, 1); |
jbe@193 | 873 // pop nil from stack if no shadow table has been found: |
jbe@193 | 874 if (lua_isnil(L, -1)) lua_pop(L, 1); |
jbe@193 | 875 // return length of argument or shadow table: |
jbe@193 | 876 lua_pushnumber(L, lua_rawlen(L, -1)); |
jbe@123 | 877 return 1; |
jbe@123 | 878 } |
jbe@123 | 879 |
jbe@175 | 880 // __index metamethod for JSON objects and JSON arrays: |
jbe@130 | 881 static int json_index(lua_State *L) { |
jbe@201 | 882 // require table as first argument: |
jbe@201 | 883 luaL_checktype(L, 1, LUA_TTABLE); |
jbe@148 | 884 // stack shall contain two function arguments: |
jbe@130 | 885 lua_settop(L, 2); |
jbe@193 | 886 // replace first argument with its shadow table |
jbe@193 | 887 // or throw error if no shadow table is found: |
jbe@193 | 888 json_getshadow(L, 1); |
jbe@139 | 889 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); |
jbe@193 | 890 lua_replace(L, 1); |
jbe@148 | 891 // use key passed as second argument to lookup value in shadow table: |
jbe@193 | 892 lua_rawget(L, 1); |
jbe@148 | 893 // if value is null-marker, then push nil onto stack: |
jbe@193 | 894 if (json_isnullmark(L, 2)) lua_pushnil(L); |
jbe@148 | 895 // return either looked up value, or nil |
jbe@127 | 896 return 1; |
jbe@127 | 897 } |
jbe@127 | 898 |
jbe@175 | 899 // __newindex metamethod for JSON objects and JSON arrays: |
jbe@130 | 900 static int json_newindex(lua_State *L) { |
jbe@201 | 901 // require table as first argument |
jbe@201 | 902 luaL_checktype(L, 1, LUA_TTABLE); |
jbe@148 | 903 // stack shall contain three function arguments: |
jbe@130 | 904 lua_settop(L, 3); |
jbe@193 | 905 // replace first argument with its shadow table |
jbe@193 | 906 // or throw error if no shadow table is found: |
jbe@193 | 907 json_getshadow(L, 1); |
jbe@130 | 908 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); |
jbe@130 | 909 lua_replace(L, 1); |
jbe@193 | 910 // second and third argument to write to shadow table: |
jbe@130 | 911 lua_rawset(L, 1); |
jbe@148 | 912 // return nothing: |
jbe@148 | 913 return 0; |
jbe@121 | 914 } |
jbe@121 | 915 |
jbe@175 | 916 // function returned as first value by json_pairs function: |
jbe@135 | 917 static int json_pairs_iterfunc(lua_State *L) { |
jbe@201 | 918 // require table as first argument |
jbe@201 | 919 luaL_checktype(L, 1, LUA_TTABLE); |
jbe@149 | 920 // stack shall contain two function arguments: |
jbe@135 | 921 lua_settop(L, 2); |
jbe@200 | 922 // get next key value pair from shadow table (argument 1) using previous key (argument 2) |
jbe@149 | 923 // and return nothing if there is no next pair: |
jbe@193 | 924 if (!lua_next(L, 1)) return 0; |
jbe@149 | 925 // replace null-marker with nil: |
jbe@155 | 926 if (json_isnullmark(L, -1)) { |
jbe@135 | 927 lua_pop(L, 1); |
jbe@135 | 928 lua_pushnil(L); |
jbe@135 | 929 } |
jbe@149 | 930 // return key and value (or key and nil, if null-marker was found): |
jbe@135 | 931 return 2; |
jbe@135 | 932 } |
jbe@135 | 933 |
jbe@149 | 934 // returns a triple such that 'for key, value in pairs(obj) do ... end' |
jbe@175 | 935 // iterates through all key value pairs (including JSON null values represented as Lua nil): |
jbe@135 | 936 static int json_pairs(lua_State *L) { |
jbe@201 | 937 // require table as first argument |
jbe@201 | 938 luaL_checktype(L, 1, LUA_TTABLE); |
jbe@200 | 939 // return triple of function json_pairs_iterfunc, shadow table of first argument, and nil: |
jbe@139 | 940 lua_pushcfunction(L, json_pairs_iterfunc); |
jbe@200 | 941 json_getshadow(L, 1); |
jbe@200 | 942 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); |
jbe@135 | 943 lua_pushnil(L); |
jbe@135 | 944 return 3; |
jbe@135 | 945 } |
jbe@135 | 946 |
jbe@175 | 947 // function returned as first value by json_ipairs function: |
jbe@134 | 948 static int json_ipairs_iterfunc(lua_State *L) { |
jbe@152 | 949 lua_Integer idx; |
jbe@201 | 950 // require table as first argument |
jbe@201 | 951 luaL_checktype(L, 1, LUA_TTABLE); |
jbe@149 | 952 // stack shall contain two function arguments: |
jbe@134 | 953 lua_settop(L, 2); |
jbe@149 | 954 // calculate new index by incrementing second argument: |
jbe@134 | 955 idx = lua_tointeger(L, 2) + 1; |
jbe@200 | 956 // do integer lookup in shadow table and store result on stack position 3: |
jbe@200 | 957 lua_rawgeti(L, 1, idx); |
jbe@149 | 958 // return nothing if there was no value: |
jbe@200 | 959 if (lua_isnil(L, 3)) return 0; |
jbe@149 | 960 // return new index and |
jbe@149 | 961 // either the looked up value if it is not equal to the null-marker |
jbe@149 | 962 // or nil instead of null-marker: |
jbe@134 | 963 lua_pushinteger(L, idx); |
jbe@200 | 964 if (json_isnullmark(L, 3)) lua_pushnil(L); |
jbe@200 | 965 else lua_pushvalue(L, 3); |
jbe@134 | 966 return 2; |
jbe@134 | 967 } |
jbe@134 | 968 |
jbe@149 | 969 // returns a triple such that 'for idx, value in ipairs(ary) do ... end' |
jbe@175 | 970 // iterates through all values (including JSON null values represented as Lua nil): |
jbe@134 | 971 static int json_ipairs(lua_State *L) { |
jbe@201 | 972 // require table as first argument |
jbe@201 | 973 luaL_checktype(L, 1, LUA_TTABLE); |
jbe@200 | 974 // return triple of function json_ipairs_iterfunc, shadow table of first argument, and zero: |
jbe@139 | 975 lua_pushcfunction(L, json_ipairs_iterfunc); |
jbe@200 | 976 json_getshadow(L, 1); |
jbe@200 | 977 if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found"); |
jbe@134 | 978 lua_pushinteger(L, 0); |
jbe@134 | 979 return 3; |
jbe@134 | 980 } |
jbe@134 | 981 |
jbe@175 | 982 // datatype representing a table key: |
jbe@175 | 983 // (used for sorting) |
jbe@163 | 984 typedef struct { |
jbe@163 | 985 size_t length; |
jbe@163 | 986 const char *data; |
jbe@163 | 987 } json_key_t; |
jbe@163 | 988 |
jbe@175 | 989 // comparation function for table keys to be passed to qsort function: |
jbe@163 | 990 static int json_key_cmp(json_key_t *key1, json_key_t *key2) { |
jbe@163 | 991 size_t pos = 0; |
jbe@163 | 992 unsigned char c1, c2; |
jbe@163 | 993 while (1) { |
jbe@163 | 994 if (key1->length > pos) { |
jbe@163 | 995 if (key2->length > pos) { |
jbe@163 | 996 c1 = key1->data[pos]; |
jbe@163 | 997 c2 = key2->data[pos]; |
jbe@163 | 998 if (c1 < c2) return -1; |
jbe@163 | 999 else if (c1 > c2) return 1; |
jbe@163 | 1000 } else { |
jbe@163 | 1001 return 1; |
jbe@163 | 1002 } |
jbe@163 | 1003 } else { |
jbe@163 | 1004 if (key2->length > pos) { |
jbe@163 | 1005 return -1; |
jbe@163 | 1006 } else { |
jbe@163 | 1007 return 0; |
jbe@163 | 1008 } |
jbe@163 | 1009 } |
jbe@163 | 1010 pos++; |
jbe@163 | 1011 } |
jbe@163 | 1012 } |
jbe@163 | 1013 |
jbe@175 | 1014 // constants for type detection of ambiguous tables: |
jbe@154 | 1015 #define JSON_TABLETYPE_UNKNOWN 0 |
jbe@154 | 1016 #define JSON_TABLETYPE_OBJECT 1 |
jbe@154 | 1017 #define JSON_TABLETYPE_ARRAY 2 |
jbe@154 | 1018 |
jbe@183 | 1019 typedef struct { |
jbe@183 | 1020 int type; |
jbe@183 | 1021 int pos; |
jbe@183 | 1022 int count; |
jbe@183 | 1023 json_key_t keys[1]; // or more |
jbe@183 | 1024 } json_container_t; |
jbe@183 | 1025 |
jbe@183 | 1026 // special Lua stack indicies for json_export function: |
jbe@183 | 1027 #define json_export_value_idx 1 |
jbe@183 | 1028 #define json_export_indentstring_idx 2 |
jbe@183 | 1029 #define json_export_objectmt_idx 3 |
jbe@183 | 1030 #define json_export_arraymt_idx 4 |
jbe@193 | 1031 #define json_export_stackswap_idx 5 |
jbe@193 | 1032 #define json_export_luacontainer_idx 6 |
jbe@193 | 1033 #define json_export_ccontainer_idx 7 |
jbe@193 | 1034 #define json_export_buffer_idx 8 |
jbe@164 | 1035 |
jbe@183 | 1036 // encodes a JSON document (passed as first argument) |
jbe@187 | 1037 // optionally using indentation (indentation string or true passed as second argument) |
jbe@183 | 1038 static int json_export(lua_State *L) { |
jbe@183 | 1039 int pretty; // pretty printing on? (i.e. printing with indentation) |
jbe@183 | 1040 luaL_Buffer buf; // Lua buffer containing result string |
jbe@183 | 1041 lua_Number num; // number to encode |
jbe@428 | 1042 char numstr[80]; // encoded number |
jbe@428 | 1043 // (21 chars needed for sign, zero, point, 17 significant digits, and NULL byte) |
jbe@428 | 1044 // (21 chars needed for sign, 19 digits INT64, and NULL byte) |
jbe@428 | 1045 // (80 chars needed for sign, 78 digits INT256, and NULL byte) |
jbe@428 | 1046 // (NOTE: we don't know the size of intmax_t and thus use 80) |
jbe@183 | 1047 const char *str; // string to encode |
jbe@183 | 1048 size_t strlen; // length of string to encode |
jbe@183 | 1049 size_t strpos ; // position in string or position of current key |
jbe@183 | 1050 unsigned char c; // character to encode (unsigned!) |
jbe@183 | 1051 char hexcode[7]; // store for unicode hex escape sequence |
jbe@183 | 1052 // NOTE: 7 bytes due to backslash, character 'u', 4 hex digits, and terminating NULL byte |
jbe@183 | 1053 int tabletype; // table type: unknown, JSON object, or JSON array |
jbe@183 | 1054 size_t keycount = 0; // number of string keys in object |
jbe@185 | 1055 json_key_t *key; // pointer to C structure containing a string key |
jbe@183 | 1056 int level = 0; // current depth level |
jbe@183 | 1057 int i; // iteration variable for level dependent repetitions |
jbe@183 | 1058 int stackswapidx = 0; // elements in stack swap table |
jbe@183 | 1059 int containerkey = 0; // temporarily set to 1, if a container key is being encoded |
jbe@183 | 1060 json_container_t *container = NULL; // pointer to current C struct for container information |
jbe@183 | 1061 // stack shall contain two function arguments: |
jbe@183 | 1062 lua_settop(L, 2); |
jbe@188 | 1063 // check if pretty printing (with indentation) is desired: |
jbe@188 | 1064 if (lua_toboolean(L, json_export_indentstring_idx)) { |
jbe@188 | 1065 // if yes, |
jbe@188 | 1066 // set pretty variable to 1: |
jbe@188 | 1067 pretty = 1; |
jbe@188 | 1068 // check if second argument is a boolean (true): |
jbe@188 | 1069 if (lua_isboolean(L, json_export_indentstring_idx)) { |
jbe@188 | 1070 // if yes, |
jbe@188 | 1071 // use default indentation if indentation argument is boolean true: |
jbe@188 | 1072 lua_pushliteral(L, " "); |
jbe@188 | 1073 lua_replace(L, json_export_indentstring_idx); |
jbe@188 | 1074 } else { |
jbe@188 | 1075 // if no, |
jbe@188 | 1076 // require second argument to be a string: |
jbe@188 | 1077 luaL_checktype(L, json_export_indentstring_idx, LUA_TSTRING); |
jbe@188 | 1078 } |
jbe@188 | 1079 } else { |
jbe@188 | 1080 // if no, |
jbe@188 | 1081 // set pretty variable to 0: |
jbe@188 | 1082 pretty = 0; |
jbe@157 | 1083 } |
jbe@183 | 1084 // push objectmt onto stack position 3: |
jbe@183 | 1085 json_regfetch(L, objectmt); |
jbe@183 | 1086 // push arraymt onto stack position 4: |
jbe@183 | 1087 json_regfetch(L, arraymt); |
jbe@193 | 1088 // push table for stack swapping onto stack position 5: |
jbe@183 | 1089 lua_newtable(L); |
jbe@193 | 1090 // create placeholders on stack positions 6 through 7: |
jbe@187 | 1091 lua_settop(L, json_export_buffer_idx); |
jbe@183 | 1092 // create Lua string buffer: |
jbe@183 | 1093 luaL_buffinit(L, &buf); |
jbe@183 | 1094 // loop: |
jbe@183 | 1095 while (1) { |
jbe@183 | 1096 // if value to encode is the null-marker, then treat it the same as nil: |
jbe@183 | 1097 if (json_isnullmark(L, json_export_value_idx)) { |
jbe@183 | 1098 lua_pushnil(L); |
jbe@183 | 1099 lua_replace(L, json_export_value_idx); |
jbe@164 | 1100 } |
jbe@183 | 1101 // distinguish between different Lua types: |
jbe@183 | 1102 switch (lua_type(L, json_export_value_idx)) { |
jbe@183 | 1103 // value to encode is nil: |
jbe@183 | 1104 case LUA_TNIL: |
jbe@183 | 1105 // add string "null" to output buffer: |
jbe@183 | 1106 luaL_addstring(&buf, "null"); |
jbe@183 | 1107 break; |
jbe@183 | 1108 // value to encode is of type number: |
jbe@183 | 1109 case LUA_TNUMBER: |
jbe@428 | 1110 #if LUA_VERSION_NUM >= 503 |
jbe@428 | 1111 // handle integers: |
jbe@428 | 1112 if (lua_isinteger(L, json_export_value_idx)) { |
jbe@428 | 1113 sprintf(numstr, "%ji", (intmax_t)lua_tointeger(L, json_export_value_idx)); |
jbe@428 | 1114 luaL_addstring(&buf, numstr); |
jbe@428 | 1115 break; |
jbe@428 | 1116 } |
jbe@428 | 1117 #endif |
jbe@183 | 1118 // convert value to double precision number: |
jbe@183 | 1119 num = lua_tonumber(L, json_export_value_idx); |
jbe@183 | 1120 // throw error if number is not-a-number: |
jbe@183 | 1121 if (isnan(num)) return luaL_error(L, "JSON export not possible for NaN value"); |
jbe@183 | 1122 // throw error if number is positive or negative infinity: |
jbe@183 | 1123 if (isinf(num)) return luaL_error(L, "JSON export not possible for infinite numbers"); |
jbe@429 | 1124 // check if float is integral: |
jbe@432 | 1125 if ((double)trunc((double)num) == (double)num) { |
jbe@433 | 1126 // use maximum precision: |
jbe@433 | 1127 sprintf(numstr, "%.17g", num); // NOTE: e.g. 12345678901234560 |
jbe@429 | 1128 } else { |
jbe@429 | 1129 // determine necessary precision to represent double precision floating point number: |
jbe@429 | 1130 sprintf(numstr, "%.15g", num); // NOTE: e.g. 0.009 should not be 0.008999999999999999 |
jbe@429 | 1131 if (strtod(numstr, NULL) != num) sprintf(numstr, "%.16g", num); |
jbe@429 | 1132 if (strtod(numstr, NULL) != num) sprintf(numstr, "%.17g", num); |
jbe@429 | 1133 } |
jbe@191 | 1134 // add string encoding of the number to the output buffer: |
jbe@189 | 1135 luaL_addstring(&buf, numstr); |
jbe@428 | 1136 #if LUA_VERSION_NUM >= 503 |
jbe@428 | 1137 // enforce trailing ".0" for floats unless exponential notation was chosen: |
jbe@428 | 1138 { |
jbe@428 | 1139 char *p; |
jbe@428 | 1140 if (numstr[0] == '-' || numstr[0] == '+') p = numstr+1; |
jbe@428 | 1141 else p = numstr; |
jbe@428 | 1142 for (; *p; p++) if (*p < '0' || *p > '9') break; |
jbe@428 | 1143 if (!*p) luaL_addstring(&buf, ".0"); |
jbe@428 | 1144 } |
jbe@428 | 1145 #endif |
jbe@183 | 1146 break; |
jbe@183 | 1147 // value to encode is of type boolean: |
jbe@183 | 1148 case LUA_TBOOLEAN: |
jbe@183 | 1149 // add string "true" or "false" according to boolean value: |
jbe@183 | 1150 luaL_addstring(&buf, lua_toboolean(L, json_export_value_idx) ? "true" : "false"); |
jbe@183 | 1151 break; |
jbe@183 | 1152 // value to encode is of type string: |
jbe@183 | 1153 case LUA_TSTRING: |
jbe@183 | 1154 // add quoted and escaped string to output buffer: |
jbe@183 | 1155 str = lua_tolstring(L, json_export_value_idx, &strlen); |
jbe@183 | 1156 luaL_addchar(&buf, '"'); |
jbe@183 | 1157 strpos = 0; |
jbe@183 | 1158 while (strpos < strlen) { |
jbe@183 | 1159 c = str[strpos++]; |
jbe@183 | 1160 if (c == '"') luaL_addstring(&buf, "\\\""); |
jbe@183 | 1161 else if (c == '\\') luaL_addstring(&buf, "\\\\"); |
jbe@183 | 1162 else if (c == 127) luaL_addstring(&buf, "\\u007F"); |
jbe@183 | 1163 else if (c >= 32) luaL_addchar(&buf, c); |
jbe@183 | 1164 else if (c == '\b') luaL_addstring(&buf, "\\b"); |
jbe@183 | 1165 else if (c == '\f') luaL_addstring(&buf, "\\f"); |
jbe@183 | 1166 else if (c == '\n') luaL_addstring(&buf, "\\n"); |
jbe@183 | 1167 else if (c == '\r') luaL_addstring(&buf, "\\r"); |
jbe@183 | 1168 else if (c == '\t') luaL_addstring(&buf, "\\t"); |
jbe@183 | 1169 else if (c == '\v') luaL_addstring(&buf, "\\v"); |
jbe@183 | 1170 else { |
jbe@183 | 1171 sprintf(hexcode, "\\u%04X", c); |
jbe@183 | 1172 luaL_addstring(&buf, hexcode); |
jbe@154 | 1173 } |
jbe@154 | 1174 } |
jbe@183 | 1175 luaL_addchar(&buf, '"'); |
jbe@183 | 1176 break; |
jbe@183 | 1177 // value to encode is of type table (this includes JSON objects and JSON arrays): |
jbe@183 | 1178 case LUA_TTABLE: |
jbe@183 | 1179 // use table's metatable to try to determine type of table: |
jbe@183 | 1180 tabletype = JSON_TABLETYPE_UNKNOWN; |
jbe@188 | 1181 if (lua_getmetatable(L, json_export_value_idx)) { |
jbe@183 | 1182 if (lua_rawequal(L, -1, json_export_objectmt_idx)) { |
jbe@183 | 1183 tabletype = JSON_TABLETYPE_OBJECT; |
jbe@183 | 1184 } else { |
jbe@183 | 1185 if (lua_rawequal(L, -1, json_export_arraymt_idx)) { |
jbe@183 | 1186 tabletype = JSON_TABLETYPE_ARRAY; |
jbe@183 | 1187 } else { |
jbe@183 | 1188 return luaL_error(L, "JSON export not possible for tables with nonsupported metatable"); |
jbe@183 | 1189 } |
jbe@183 | 1190 } |
jbe@183 | 1191 // reset stack (pop metatable from stack): |
jbe@183 | 1192 lua_pop(L, 1); |
jbe@183 | 1193 } |
jbe@193 | 1194 // replace table with its shadow table if existent: |
jbe@193 | 1195 json_getshadow(L, json_export_value_idx); |
jbe@183 | 1196 if (lua_isnil(L, -1)) lua_pop(L, 1); |
jbe@193 | 1197 else lua_replace(L, json_export_value_idx); |
jbe@187 | 1198 // check if type of table is still undetermined |
jbe@187 | 1199 // and optionally calculate number of string keys (keycount) |
jbe@187 | 1200 // or set keycount to zero: |
jbe@186 | 1201 keycount = 0; |
jbe@183 | 1202 if (tabletype == JSON_TABLETYPE_UNKNOWN) { |
jbe@187 | 1203 // if type of table is undetermined, |
jbe@187 | 1204 // iterate over all keys: |
jbe@188 | 1205 for (lua_pushnil(L); lua_next(L, json_export_value_idx); lua_pop(L, 1)) { |
jbe@183 | 1206 switch (lua_type(L, -2)) { |
jbe@183 | 1207 case LUA_TSTRING: |
jbe@183 | 1208 // for string keys, |
jbe@183 | 1209 // increase keycount (may avoid another iteration): |
jbe@183 | 1210 keycount++; |
jbe@183 | 1211 // if type of table was unknown, then type of table is a JSON object now: |
jbe@183 | 1212 if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_OBJECT; |
jbe@183 | 1213 // if type of table was a JSON array, then the type of table is ambiguous now |
jbe@183 | 1214 // and an error is thrown: |
jbe@183 | 1215 else if (tabletype == JSON_TABLETYPE_ARRAY) goto json_export_tabletype_error; |
jbe@183 | 1216 break; |
jbe@183 | 1217 case LUA_TNUMBER: |
jbe@183 | 1218 // for numeric keys, |
jbe@183 | 1219 // if type of table was unknown, then type of table is a JSON array now: |
jbe@183 | 1220 if (tabletype == JSON_TABLETYPE_UNKNOWN) tabletype = JSON_TABLETYPE_ARRAY; |
jbe@183 | 1221 // if type of table was a JSON object, then the type of table is ambiguous now |
jbe@183 | 1222 // and an error is thrown: |
jbe@183 | 1223 else if (tabletype == JSON_TABLETYPE_OBJECT) goto json_export_tabletype_error; |
jbe@183 | 1224 break; |
jbe@183 | 1225 } |
jbe@164 | 1226 } |
jbe@163 | 1227 } |
jbe@183 | 1228 // raise error if too many nested levels: |
jbe@183 | 1229 if (level >= JSON_MAXDEPTH) { |
jbe@183 | 1230 return luaL_error(L, "More than %d nested JSON levels", JSON_MAXDEPTH); |
jbe@163 | 1231 } |
jbe@183 | 1232 // store previous container information (if existent) on stack swap |
jbe@183 | 1233 // and increase level variable: |
jbe@183 | 1234 if (level++) { |
jbe@183 | 1235 lua_pushvalue(L, json_export_luacontainer_idx); |
jbe@183 | 1236 lua_rawseti(L, json_export_stackswap_idx, ++stackswapidx); |
jbe@183 | 1237 lua_pushvalue(L, json_export_ccontainer_idx); |
jbe@183 | 1238 lua_rawseti(L, json_export_stackswap_idx, ++stackswapidx); |
jbe@183 | 1239 } |
jbe@188 | 1240 // use value as current container: |
jbe@188 | 1241 lua_pushvalue(L, json_export_value_idx); |
jbe@188 | 1242 lua_replace(L, json_export_luacontainer_idx); |
jbe@183 | 1243 // distinguish between JSON objects and JSON arrays: |
jbe@183 | 1244 switch (tabletype) { |
jbe@183 | 1245 // JSON object: |
jbe@183 | 1246 case JSON_TABLETYPE_OBJECT: |
jbe@183 | 1247 // calculate count of string keys unless it has been calculated before: |
jbe@183 | 1248 if (!keycount) { |
jbe@183 | 1249 for (lua_pushnil(L); lua_next(L, json_export_luacontainer_idx); lua_pop(L, 1)) { |
jbe@183 | 1250 if (lua_type(L, -2) == LUA_TSTRING) keycount++; |
jbe@164 | 1251 } |
jbe@164 | 1252 } |
jbe@186 | 1253 // allocate memory for C structure containing string keys and container iteration state: |
jbe@186 | 1254 container = lua_newuserdata(L, sizeof(json_container_t) + (keycount-1) * sizeof(json_key_t)); |
jbe@187 | 1255 // store reference to C structure on designated stack position: |
jbe@186 | 1256 lua_replace(L, json_export_ccontainer_idx); |
jbe@186 | 1257 // initialize C structure for container state: |
jbe@186 | 1258 container->type = JSON_TABLETYPE_OBJECT; |
jbe@186 | 1259 container->count = keycount; |
jbe@186 | 1260 container->pos = 0; |
jbe@187 | 1261 // check if object contains any keys: |
jbe@183 | 1262 if (keycount) { |
jbe@187 | 1263 // if yes, |
jbe@187 | 1264 // copy all string keys to the C structure (and reset container->pos again): |
jbe@183 | 1265 for (lua_pushnil(L); lua_next(L, json_export_luacontainer_idx); lua_pop(L, 1)) { |
jbe@183 | 1266 if (lua_type(L, -2) == LUA_TSTRING) { |
jbe@183 | 1267 json_key_t *key = &container->keys[container->pos++]; |
jbe@183 | 1268 key->data = lua_tolstring(L, -2, &key->length); |
jbe@183 | 1269 } |
jbe@183 | 1270 } |
jbe@183 | 1271 container->pos = 0; |
jbe@183 | 1272 // sort C array using quicksort: |
jbe@183 | 1273 qsort(container->keys, keycount, sizeof(json_key_t), (void *)json_key_cmp); |
jbe@154 | 1274 } |
jbe@183 | 1275 // add opening bracket to output buffer: |
jbe@183 | 1276 luaL_addchar(&buf, '{'); |
jbe@183 | 1277 break; |
jbe@183 | 1278 // JSON array: |
jbe@183 | 1279 case JSON_TABLETYPE_ARRAY: |
jbe@187 | 1280 // allocate memory for C structure for container iteration state: |
jbe@183 | 1281 container = lua_newuserdata(L, sizeof(json_container_t) - sizeof(json_key_t)); |
jbe@187 | 1282 // store reference to C structure on designated stack position: |
jbe@183 | 1283 lua_replace(L, json_export_ccontainer_idx); |
jbe@187 | 1284 // initialize C structure for container state: |
jbe@183 | 1285 container->type = JSON_TABLETYPE_ARRAY; |
jbe@183 | 1286 container->pos = 0; |
jbe@183 | 1287 // add opening bracket to output buffer: |
jbe@183 | 1288 luaL_addchar(&buf, '['); |
jbe@183 | 1289 break; |
jbe@183 | 1290 default: |
jbe@183 | 1291 // throw error if table type is unknown: |
jbe@183 | 1292 json_export_tabletype_error: |
jbe@183 | 1293 return luaL_error(L, "JSON export not possible for ambiguous table (cannot decide whether it is an object or array)"); |
jbe@183 | 1294 } |
jbe@183 | 1295 break; |
jbe@183 | 1296 default: |
jbe@183 | 1297 // all other datatypes are considered an error: |
jbe@183 | 1298 return luaL_error(L, "JSON export not possible for values of type \"%s\"", lua_typename(L, lua_type(L, json_export_value_idx))); |
jbe@183 | 1299 } |
jbe@187 | 1300 // check if a container is being processed: |
jbe@183 | 1301 if (container) { |
jbe@187 | 1302 // if yes, |
jbe@187 | 1303 // execute code for container iteration: |
jbe@184 | 1304 json_export_container: |
jbe@187 | 1305 // distinguish between JSON objects and JSON arrays: |
jbe@183 | 1306 switch (container->type) { |
jbe@187 | 1307 // JSON object: |
jbe@183 | 1308 case JSON_TABLETYPE_OBJECT: |
jbe@187 | 1309 // finish iteration if all string keys have been processed: |
jbe@185 | 1310 if (container->pos == container->count) goto json_export_close; |
jbe@187 | 1311 // push current string key on top of stack: |
jbe@185 | 1312 key = &container->keys[container->pos]; |
jbe@185 | 1313 lua_pushlstring(L, key->data, key->length); |
jbe@187 | 1314 // check if the key has already been exported: |
jbe@185 | 1315 if (!containerkey) { |
jbe@187 | 1316 // if no, |
jbe@187 | 1317 // add a comma to the output buffer if necessary: |
jbe@185 | 1318 if (container->pos) luaL_addchar(&buf, ','); |
jbe@187 | 1319 // set containerkey variable to true: |
jbe@185 | 1320 containerkey = 1; |
jbe@183 | 1321 } else { |
jbe@187 | 1322 // if a key has already been exported, |
jbe@187 | 1323 // add a colon to the output buffer: |
jbe@185 | 1324 luaL_addchar(&buf, ':'); |
jbe@187 | 1325 // add a space to the output buffer for pretty results: |
jbe@185 | 1326 if (pretty) luaL_addchar(&buf, ' '); |
jbe@187 | 1327 // replace string key on top of stack with corresponding value: |
jbe@185 | 1328 lua_rawget(L, json_export_luacontainer_idx); |
jbe@187 | 1329 // reset containerkey variable |
jbe@185 | 1330 containerkey = 0; |
jbe@187 | 1331 // increase number of processed key value pairs: |
jbe@185 | 1332 container->pos++; |
jbe@163 | 1333 } |
jbe@187 | 1334 // store key or value on top of stack in designated stack position: |
jbe@185 | 1335 lua_replace(L, json_export_value_idx); |
jbe@183 | 1336 break; |
jbe@187 | 1337 // JSON array: |
jbe@183 | 1338 case JSON_TABLETYPE_ARRAY: |
jbe@187 | 1339 // store next value in designated stack position: |
jbe@185 | 1340 lua_rawgeti(L, json_export_luacontainer_idx, container->pos+1); |
jbe@183 | 1341 lua_replace(L, json_export_value_idx); |
jbe@187 | 1342 // finish iteration if value is nil: |
jbe@185 | 1343 if (lua_isnil(L, json_export_value_idx)) goto json_export_close; |
jbe@187 | 1344 // add a comma to the output buffer if necessary: |
jbe@185 | 1345 if (container->pos) luaL_addchar(&buf, ','); |
jbe@187 | 1346 // increase number of processed values: |
jbe@185 | 1347 container->pos++; |
jbe@183 | 1348 break; |
jbe@187 | 1349 // common code for closing JSON objects or JSON arrays: |
jbe@183 | 1350 json_export_close: |
jbe@187 | 1351 // decrement level variable: |
jbe@185 | 1352 level--; |
jbe@187 | 1353 // handle indentation for pretty results: |
jbe@187 | 1354 if (pretty && container->pos) { |
jbe@187 | 1355 luaL_addchar(&buf, '\n'); |
jbe@187 | 1356 for (i=0; i<level; i++) { |
jbe@187 | 1357 lua_pushvalue(L, json_export_indentstring_idx); |
jbe@187 | 1358 luaL_addvalue(&buf); |
jbe@185 | 1359 } |
jbe@164 | 1360 } |
jbe@187 | 1361 // add closing bracket to output buffer: |
jbe@185 | 1362 luaL_addchar(&buf, container->type == JSON_TABLETYPE_OBJECT ? '}' : ']'); |
jbe@187 | 1363 // finish export if last level has been closed: |
jbe@185 | 1364 if (!level) goto json_export_finish; |
jbe@187 | 1365 // otherwise, |
jbe@187 | 1366 // recall previous container information from stack swap |
jbe@187 | 1367 // and set C pointer to corresponding C struct: |
jbe@185 | 1368 lua_rawgeti(L, json_export_stackswap_idx, stackswapidx--); |
jbe@185 | 1369 lua_replace(L, json_export_ccontainer_idx); |
jbe@185 | 1370 container = lua_touserdata(L, json_export_ccontainer_idx); |
jbe@185 | 1371 lua_rawgeti(L, json_export_stackswap_idx, stackswapidx--); |
jbe@185 | 1372 lua_replace(L, json_export_luacontainer_idx); |
jbe@187 | 1373 // repeat code for container iteration: |
jbe@185 | 1374 goto json_export_container; |
jbe@164 | 1375 } |
jbe@184 | 1376 // handle indentation for pretty results: |
jbe@185 | 1377 if (pretty && (containerkey || container->type == JSON_TABLETYPE_ARRAY)) { |
jbe@185 | 1378 luaL_addchar(&buf, '\n'); |
jbe@185 | 1379 for (i=0; i<level; i++) { |
jbe@185 | 1380 lua_pushvalue(L, json_export_indentstring_idx); |
jbe@185 | 1381 luaL_addvalue(&buf); |
jbe@164 | 1382 } |
jbe@154 | 1383 } |
jbe@183 | 1384 } else { |
jbe@187 | 1385 // if no container is being processed, |
jbe@187 | 1386 // finish export: |
jbe@185 | 1387 json_export_finish: |
jbe@185 | 1388 // for pretty results, add final newline character if outermost container is processed: |
jbe@185 | 1389 if (pretty) luaL_addchar(&buf, '\n'); |
jbe@187 | 1390 // create and return Lua string from buffer contents |
jbe@154 | 1391 luaL_pushresult(&buf); |
jbe@154 | 1392 return 1; |
jbe@154 | 1393 } |
jbe@154 | 1394 } |
jbe@164 | 1395 } |
jbe@164 | 1396 |
jbe@149 | 1397 // functions in library module: |
jbe@121 | 1398 static const struct luaL_Reg json_module_functions[] = { |
jbe@133 | 1399 {"object", json_object}, |
jbe@173 | 1400 {"array", json_array}, |
jbe@121 | 1401 {"import", json_import}, |
jbe@154 | 1402 {"export", json_export}, |
jbe@173 | 1403 {"get", json_get}, |
jbe@173 | 1404 {"type", json_type}, |
jbe@173 | 1405 {"set", json_set}, |
jbe@121 | 1406 {NULL, NULL} |
jbe@121 | 1407 }; |
jbe@121 | 1408 |
jbe@149 | 1409 // metamethods for JSON objects, JSON arrays, and unknown JSON collections (object or array): |
jbe@126 | 1410 static const struct luaL_Reg json_metatable_functions[] = { |
jbe@130 | 1411 {"__len", json_len}, |
jbe@130 | 1412 {"__index", json_index}, |
jbe@130 | 1413 {"__newindex", json_newindex}, |
jbe@135 | 1414 {"__pairs", json_pairs}, |
jbe@134 | 1415 {"__ipairs", json_ipairs}, |
jbe@160 | 1416 {"__tostring", json_export}, |
jbe@126 | 1417 {NULL, NULL} |
jbe@126 | 1418 }; |
jbe@126 | 1419 |
jbe@157 | 1420 // metamethods for JSON null marker: |
jbe@157 | 1421 static const struct luaL_Reg json_nullmark_metamethods[] = { |
jbe@157 | 1422 {"__tostring", json_nullmark_tostring}, |
jbe@157 | 1423 {NULL, NULL} |
jbe@157 | 1424 }; |
jbe@157 | 1425 |
jbe@149 | 1426 // initializes json library: |
jbe@121 | 1427 int luaopen_json(lua_State *L) { |
jbe@149 | 1428 // empty stack: |
jbe@126 | 1429 lua_settop(L, 0); |
jbe@149 | 1430 // push library module onto stack position 1: |
jbe@149 | 1431 lua_newtable(L); |
jbe@149 | 1432 // register library functions: |
jbe@149 | 1433 luaL_setfuncs(L, json_module_functions, 0); |
jbe@149 | 1434 // create and store objectmt: |
jbe@138 | 1435 lua_newtable(L); |
jbe@138 | 1436 luaL_setfuncs(L, json_metatable_functions, 0); |
jbe@144 | 1437 json_regstore(L, objectmt); |
jbe@149 | 1438 // create and store arraymt: |
jbe@138 | 1439 lua_newtable(L); |
jbe@138 | 1440 luaL_setfuncs(L, json_metatable_functions, 0); |
jbe@144 | 1441 json_regstore(L, arraymt); |
jbe@157 | 1442 // set metatable of null marker and make it available through library module: |
jbe@157 | 1443 json_pushnullmark(L); |
jbe@157 | 1444 lua_newtable(L); |
jbe@157 | 1445 luaL_setfuncs(L, json_nullmark_metamethods, 0); |
jbe@157 | 1446 lua_setmetatable(L, -2); |
jbe@157 | 1447 lua_setfield(L, 1, "null"); |
jbe@157 | 1448 // return library module (that's expected on top of stack): |
jbe@121 | 1449 return 1; |
jbe@121 | 1450 } |