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