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