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