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