webmcp
view libraries/json/json.c @ 154:c8669dde9ce2
Added JSON export function
| author | jbe | 
|---|---|
| date | Thu Jul 31 03:01:53 2014 +0200 (2014-07-31) | 
| parents | c8c91216255f | 
| children | 185e944182cb | 
 line source
     1 #include <lua.h>
     2 #include <lauxlib.h>
     3 #include <stdlib.h>
     4 #include <string.h>
     5 #include <math.h>
     7 // maximum number of nested JSON values (objects and arrays):
     8 // NOTE: The Lua reference states that the stack may typically contain at least
     9 //       "a few thousand elements". Since every nested level consumes
    10 //       3 elements on the Lua stack (the object/array, its shadow table,
    11 //       a string key or a placeholder), we limit the number of nested levels
    12 //       to 500. If a stack overflow would still happen in the import function,
    13 //       this is detected nevertheless and an error is thrown (instead of
    14 //       returning nil and an error string).
    15 #define JSON_MAXDEPTH 500
    17 // macros for usage of Lua registry:
    18 #define JSON_REGENT char
    19 #define JSON_REGPOINTER void *
    20 #define json_pushlightref(L, x) lua_pushlightuserdata((L), &json_reference.x)
    21 #define json_islightref(L, i, x) (lua_touserdata((L), (i)) == &json_reference.x)
    22 #define json_regpointer(x) (&json_registry.x)
    23 #define json_regfetchpointer(L, x) lua_rawgetp((L), LUA_REGISTRYINDEX, (x))
    24 #define json_regfetch(L, x) json_regfetchpointer(L, json_regpointer(x))
    25 #define json_regstore(L, x) lua_rawsetp(L, LUA_REGISTRYINDEX, json_regpointer(x))
    27 // generate dummy memory addresses that represent non-modifiable lightuserdata (dummy) objects:
    28 static struct {
    29   JSON_REGENT nullmark;  // magic value to indicate JSON null value in shadow table
    30 } json_reference;
    32 // generate dummy memory addresses that represent Lua objects
    33 // via lightuserdata keys and LUA_REGISTRYINDEX:
    34 static struct {
    35   JSON_REGENT shadowtbl;  // ephemeron table that maps tables to their corresponding shadow table
    36   JSON_REGENT unknownmt;  // metatable for tables that may be either JSON objects or JSON arrays
    37   JSON_REGENT objectmt;   // metatable for JSON objects
    38   JSON_REGENT arraymt;    // metatable for JSON arrays
    39 } json_registry;
    41 // marks a Lua table as JSON object or JSON array:
    42 // (returns its modified argument or a new table if argument is nil)
    43 static int json_mark(lua_State *L, JSON_REGPOINTER mt) {
    44   // check if argument is nil
    45   if (lua_isnoneornil(L, 1)) {
    46     // create new table at stack position 1:
    47     lua_settop(L, 0);
    48     lua_newtable(L);
    49     // create shadow table (leaving previously created table on stack position 1):
    50     json_regfetch(L, shadowtbl);
    51     lua_pushvalue(L, 1);
    52     lua_newtable(L);
    53     lua_rawset(L, -3);
    54   } else {
    55     // push shadow table on top of stack:
    56     json_regfetch(L, shadowtbl);
    57     lua_pushvalue(L, 1);
    58     lua_rawget(L, -2);
    59     // if shadow table does not exist:
    60     if (lua_isnil(L, -1)) {
    61       // create shadow table and leave it on top of stack:
    62       lua_newtable(L);
    63       lua_pushvalue(L, 1);
    64       lua_pushvalue(L, -2);
    65       lua_rawset(L, -5);
    66     }
    67     // move elements from original table to shadow table (that's expected on top of stack):
    68     for(lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
    69       lua_pushvalue(L, -2);
    70       lua_pushnil(L);
    71       lua_rawset(L, 1);
    72       lua_pushvalue(L, -2);
    73       lua_pushvalue(L, -2);
    74       lua_rawset(L, -5);
    75     }
    76   }
    77   // discard everything but table to return:
    78   lua_settop(L, 1);
    79   // set metatable:
    80   json_regfetchpointer(L, mt);
    81   lua_setmetatable(L, 1);
    82   // return table:
    83   return 1;
    84 }
    86 // marks a table as JSON object:
    87 // (returns its modified argument or a new table if argument is nil)
    88 static int json_object(lua_State *L) {
    89   return json_mark(L, json_regpointer(objectmt));
    90 }
    92 // marks a table as JSON array:
    93 // (returns its modified argument or a new table if argument is nil)
    94 static int json_array(lua_State *L) {
    95   return json_mark(L, json_regpointer(arraymt));
    96 }
    98 // internal states of JSON parser:
    99 #define JSON_STATE_VALUE 0
   100 #define JSON_STATE_OBJECT_KEY 1
   101 #define JSON_STATE_OBJECT_KEY_TERMINATOR 2
   102 #define JSON_STATE_OBJECT_VALUE 3
   103 #define JSON_STATE_OBJECT_SEPARATOR 4
   104 #define JSON_STATE_ARRAY_VALUE 5
   105 #define JSON_STATE_ARRAY_SEPARATOR 6
   106 #define JSON_STATE_END 7
   108 // special Lua stack indicies for json_import function:
   109 #define json_import_objectmt_idx 2
   110 #define json_import_arraymt_idx 3
   111 #define json_import_shadowtbl_idx 4
   112 #define json_import_nullmark_idx 5
   114 // decodes a JSON document:
   115 static int json_import(lua_State *L) {
   116   const char *str;   // string to parse
   117   size_t total;      // total length of string to parse
   118   size_t pos = 0;    // current position in string to parse
   119   size_t level = 0;  // nested levels of objects/arrays currently being processed
   120   int mode = JSON_STATE_VALUE;  // state of parser (i.e. "what's expected next?")
   121   char c;              // variable to store a single character to be processed
   122   luaL_Buffer luabuf;  // Lua buffer to decode JSON string values
   123   char *cbuf;          // C buffer to decode JSON string values
   124   size_t writepos;     // write position of decoded strings in C buffer
   125   size_t arraylen;     // variable to temporarily store the array length
   126   // stack shall contain one function argument:
   127   lua_settop(L, 1);
   128   // push objectmt onto stack position 2:
   129   json_regfetch(L, objectmt);
   130   // push arraymt onto stack position 3:
   131   json_regfetch(L, arraymt);
   132   // push shadowtbl onto stack position 4:
   133   json_regfetch(L, shadowtbl);
   134   // push nullmark onto stack position 5:
   135   json_pushlightref(L, nullmark);
   136   // require string as first argument:
   137   str = luaL_checklstring(L, 1, &total);
   138   // if string contains a NULL byte, this is a syntax error
   139   if (strlen(str) != total) goto json_import_syntax_error;
   140   // main loop of parser:
   141   json_import_loop:
   142   // skip whitespace and store next character in variable 'c':
   143   while (c = str[pos],
   144     c == ' ' ||
   145     c == '\f' ||
   146     c == '\n' ||
   147     c == '\r' ||
   148     c == '\t' ||
   149     c == '\v'
   150   ) pos++;
   151   // switch statement to handle certain (single) characters:
   152   switch (c) {
   153   // handle end of JSON document:
   154   case 0:
   155     // if end of JSON document was expected, then return top element of stack as result:
   156     if (mode == JSON_STATE_END) return 1;
   157     // otherwise, the JSON document was malformed:
   158     json_import_unexpected_eof:
   159     lua_pushnil(L);
   160     if (level == 0) lua_pushliteral(L, "Empty string");
   161     else lua_pushliteral(L, "Unexpected end of JSON document");
   162     return 2;
   163   // new JSON object:
   164   case '{':
   165     // if a JSON object is not expected here, then return an error:
   166     if (
   167       mode != JSON_STATE_VALUE &&
   168       mode != JSON_STATE_OBJECT_VALUE &&
   169       mode != JSON_STATE_ARRAY_VALUE
   170     ) goto json_import_syntax_error;
   171     // create JSON object on stack:
   172     lua_newtable(L);
   173     // set metatable of JSON object:
   174     lua_pushvalue(L, json_import_objectmt_idx);
   175     lua_setmetatable(L, -2);
   176     // create internal shadow table on stack:
   177     lua_newtable(L);
   178     // register internal shadow table:
   179     lua_pushvalue(L, -2);
   180     lua_pushvalue(L, -2);
   181     lua_rawset(L, json_import_shadowtbl_idx);
   182     // expect object key (or end of object) to follow:
   183     mode = JSON_STATE_OBJECT_KEY;
   184     // jump to common code for opening JSON object and JSON array:
   185     goto json_import_open;
   186   // new JSON array:
   187   case '[':
   188     // if a JSON array is not expected here, then return an error:
   189     if (
   190       mode != JSON_STATE_VALUE &&
   191       mode != JSON_STATE_OBJECT_VALUE &&
   192       mode != JSON_STATE_ARRAY_VALUE
   193     ) goto json_import_syntax_error;
   194     // create JSON array on stack:
   195     lua_newtable(L);
   196     // set metatable of JSON array:
   197     lua_pushvalue(L, json_import_arraymt_idx);
   198     lua_setmetatable(L, -2);
   199     // create internal shadow table on stack:
   200     lua_newtable(L);
   201     // register internal shadow table:
   202     lua_pushvalue(L, -2);
   203     lua_pushvalue(L, -2);
   204     lua_rawset(L, json_import_shadowtbl_idx);
   205     // add nil as key (needed to keep stack balance) and as magic to detect arrays:
   206     lua_pushnil(L);
   207     // expect array value (or end of array) to follow:
   208     mode = JSON_STATE_ARRAY_VALUE;
   209     // continue with common code for opening JSON object and JSON array:
   210   // common code for opening JSON object or JSON array:
   211   json_import_open:
   212     // limit nested levels:
   213     if (level >= JSON_MAXDEPTH) {
   214       lua_pushnil(L);
   215       lua_pushliteral(L, "Too many nested JSON levels");
   216       return 2;
   217     }
   218     // additional buffer overflow protection:
   219     if (!lua_checkstack(L, LUA_MINSTACK))
   220       return luaL_error(L, "Caught stack overflow in JSON import function (too many nested levels and stack size too small)");
   221     // increment level:
   222     level++;
   223     // consume input character:
   224     pos++;
   225     goto json_import_loop;
   226   // end of JSON object:
   227   case '}':
   228     // if end of JSON object is not expected here, then return an error:
   229     if (
   230       mode != JSON_STATE_OBJECT_KEY &&
   231       mode != JSON_STATE_OBJECT_SEPARATOR
   232     ) goto json_import_syntax_error;
   233     // jump to common code for end of JSON object and JSON array:
   234     goto json_import_close;
   235   // end of JSON array:
   236   case ']':
   237     // if end of JSON array is not expected here, then return an error:
   238     if (
   239       mode != JSON_STATE_ARRAY_VALUE &&
   240       mode != JSON_STATE_ARRAY_SEPARATOR
   241     ) goto json_import_syntax_error;
   242     // pop nil key/magic (that was needed to keep stack balance):
   243     lua_pop(L, 1);
   244     // continue with common code for end of JSON object and JSON array:
   245   // common code for end of JSON object or JSON array:
   246   json_import_close:
   247     // consume input character:
   248     pos++;
   249     // pop shadow table:
   250     lua_pop(L, 1);
   251     // check if nested:
   252     if (--level) {
   253       // if nested,
   254       // check if outer(!) structure is an array or object:
   255       if (lua_isnil(L, -2)) {
   256         // select array value processing:
   257         mode = JSON_STATE_ARRAY_VALUE;
   258       } else {
   259         // select object value processing:
   260         mode = JSON_STATE_OBJECT_VALUE;
   261       }
   262       // store value in outer structure:
   263       goto json_import_process_value;
   264     }
   265     // if not nested, then expect end of JSON document and continue with loop:
   266     mode = JSON_STATE_END;
   267     goto json_import_loop;
   268   // key terminator:
   269   case ':':
   270     // if key terminator is not expected here, then return an error:
   271     if (mode != JSON_STATE_OBJECT_KEY_TERMINATOR)
   272       goto json_import_syntax_error;
   273     // consume input character:
   274     pos++;
   275     // expect object value to follow:
   276     mode = JSON_STATE_OBJECT_VALUE;
   277     // continue with loop:
   278     goto json_import_loop;
   279   // value terminator (NOTE: trailing comma at end of value or key-value list is tolerated by this parser)
   280   case ',':
   281     // branch according to parser state:
   282     if (mode == JSON_STATE_OBJECT_SEPARATOR) {
   283       // expect an object key to follow:
   284       mode = JSON_STATE_OBJECT_KEY;
   285     } else if (mode == JSON_STATE_ARRAY_SEPARATOR) {
   286       // expect an array value to follow:
   287       mode = JSON_STATE_ARRAY_VALUE;
   288     } else {
   289        // if value terminator is not expected here, then return an error:
   290        goto json_import_syntax_error;
   291     }
   292     // consume input character:
   293     pos++;
   294     // continue with loop:
   295     goto json_import_loop;
   296   // string literal:
   297   case '"':
   298     // consume quote character:
   299     pos++;
   300     // prepare buffer to decode string (with maximum possible length) and set write position to zero:
   301     cbuf = luaL_buffinitsize(L, &luabuf, total-pos);
   302     writepos = 0;
   303     // loop through the characters until encountering end quote:
   304     while ((c = str[pos++]) != '"') {
   305       if (c == 0) {
   306         // handle unexpected end of JSON document:
   307         goto json_import_unexpected_eof;
   308       } else if (c < 32 || c == 127) {
   309         // do not allow ASCII control characters:
   310         // NOTE: illegal UTF-8 sequences and extended control characters are not sanitized
   311         //       by this parser to allow different encodings than Unicode
   312         lua_pushnil(L);
   313         lua_pushliteral(L, "Unexpected control character in JSON string");
   314         return 2;
   315       } else if (c == '\\') {
   316         // read next char after backslash escape:
   317         c = str[pos++];
   318         switch (c) {
   319         // unexpected end-of-string:
   320         case 0:
   321           goto json_import_unexpected_eof;
   322         // unescaping of quotation mark, slash, and backslash:
   323         case '"':
   324         case '/':
   325         case '\\':
   326           cbuf[writepos++] = c;
   327           break;
   328         // unescaping of backspace:
   329         case 'b': cbuf[writepos++] = '\b'; break;
   330         // unescaping of form-feed:
   331         case 'f': cbuf[writepos++] = '\f'; break;
   332         // unescaping of new-line:
   333         case 'n': cbuf[writepos++] = '\n'; break;
   334         // unescaping of carriage-return:
   335         case 'r': cbuf[writepos++] = '\r'; break;
   336         // unescaping of tabulator:
   337         case 't': cbuf[writepos++] = '\t'; break;
   338         // unescaping of UTF-16 characters
   339         case 'u':
   340           lua_pushnil(L);
   341           lua_pushliteral(L, "JSON unicode escape sequences are not implemented yet");  // TODO
   342           return 2;
   343         // unexpected escape sequence:
   344         default:
   345           lua_pushnil(L);
   346           lua_pushliteral(L, "Unexpected string escape sequence in JSON document");
   347           return 2;
   348         }
   349       } else {
   350         // normal character:
   351         cbuf[writepos++] = c;
   352       }
   353     }
   354     // process buffer to Lua string:
   355     luaL_pushresultsize(&luabuf, writepos);
   356     // continue with processing of decoded string:
   357     goto json_import_process_value;
   358   }
   359   // process values whose type is is not deducible from a single character:
   360   if ((c >= '0' && c <= '9') || c == '-' || c == '+') {
   361     // for numbers,
   362     // use strtod() call to parse a (double precision) floating point number:
   363     char *endptr;
   364     double numval;
   365     numval = strtod(str+pos, &endptr);
   366     // catch parsing errors:
   367     if (endptr == str+pos) goto json_import_syntax_error;
   368     // consume characters that were parsed:
   369     pos += endptr - (str+pos);
   370     // push parsed (double precision) floating point number on Lua stack:
   371     lua_pushnumber(L, numval);
   372   } else if (!strncmp(str+pos, "true", 4)) {
   373     // consume 4 input characters for "true":
   374     pos += 4;
   375     // put Lua true value onto stack:
   376     lua_pushboolean(L, 1);
   377   } else if (!strncmp(str+pos, "false", 5)) {
   378     // consume 5 input characters for "false":
   379     pos += 5;
   380     // put Lua false value onto stack:
   381     lua_pushboolean(L, 0);
   382   } else if (!strncmp(str+pos, "null", 4)) {
   383     // consume 4 input characters for "null":
   384     pos += 4;
   385     // different behavor for top-level and sub-levels:
   386     if (level) {
   387       // if sub-level,
   388       // push special null-marker onto stack:
   389       lua_pushvalue(L, json_import_nullmark_idx);
   390     } else {
   391       // if top-level,
   392       // push nil onto stack:
   393       lua_pushnil(L);
   394     }
   395   } else {
   396     // all other cases are a syntax error:
   397     goto json_import_syntax_error;
   398   }
   399   // process a decoded value or key value pair (expected on top of Lua stack):
   400   json_import_process_value:
   401   switch (mode) {
   402   // an object key has been read:
   403   case JSON_STATE_OBJECT_KEY:
   404     // if an object key is not a string, then this is a syntax error:
   405     if (lua_type(L, -1) != LUA_TSTRING) goto json_import_syntax_error;
   406     // expect key terminator to follow:
   407     mode = JSON_STATE_OBJECT_KEY_TERMINATOR;
   408     // continue with loop:
   409     goto json_import_loop;
   410   // a key value pair has been read:
   411   case JSON_STATE_OBJECT_VALUE:
   412     // store key value pair in outer shadow table:
   413     lua_rawset(L, -3);
   414     // expect value terminator (or end of object) to follow:
   415     mode = JSON_STATE_OBJECT_SEPARATOR;
   416     // continue with loop:
   417     goto json_import_loop;
   418   // an array value has been read:
   419   case JSON_STATE_ARRAY_VALUE:
   420     // get current array length:
   421     arraylen = lua_rawlen(L, -3);
   422     // throw error if array would exceed INT_MAX elements:
   423     // TODO: Lua 5.3 may support more elements
   424     if (arraylen >= INT_MAX) {
   425       lua_pushnil(L);
   426       lua_pushfstring(L, "Array exceeded length of %d elements", INT_MAX);
   427     }
   428     // store value in outer shadow table:
   429     lua_rawseti(L, -3, arraylen + 1);
   430     // expect value terminator (or end of object) to follow:
   431     mode = JSON_STATE_ARRAY_SEPARATOR;
   432     // continue with loop
   433     goto json_import_loop;
   434   // a single value has been read:
   435   case JSON_STATE_VALUE:
   436     // leave value on top of stack, expect end of JSON document, and continue with loop:
   437     mode = JSON_STATE_END;
   438     goto json_import_loop;
   439   }
   440   // syntax error handling (reachable by goto statement):
   441   json_import_syntax_error:
   442   lua_pushnil(L);
   443   lua_pushliteral(L, "Syntax error in JSON document");
   444   return 2;
   445 }
   447 // special Lua stack indicies for json_path function:
   448 #define json_path_shadowtbl_idx 1
   449 #define json_path_nullmark_idx 2
   451 // stack offset of arguments to json_path function:
   452 #define json_path_idxshift 2
   454 // gets a value or its type from a JSON document (passed as first argument)
   455 // using a path (passed as variable number of keys after first argument):
   456 static int json_path(lua_State *L, int type_mode) {
   457   int stacktop;                      // stack index of top of stack (after shifting)
   458   int idx = 2 + json_path_idxshift;  // stack index of current argument to process
   459   // insert shadowtbl into stack at position 1 (shifting the arguments):
   460   json_regfetch(L, shadowtbl);
   461   lua_insert(L, 1);
   462   // insert nullmark into stack at position 2 (shifting the arguments):
   463   json_pushlightref(L, nullmark);
   464   lua_insert(L, 2);
   465   // store stack index of top of stack:
   466   stacktop = lua_gettop(L);
   467   // use first argument as "current value" (stored on top of stack):
   468   lua_pushvalue(L, 1 + json_path_idxshift);
   469   // process each "path key" (2nd argument and following arguments):
   470   while (idx <= stacktop) {
   471     // if "current value" (on top of stack) is nil, then the path cannot be walked and nil is returned:
   472     if (lua_isnil(L, -1)) return 1;
   473     // try to get shadow table of "current value":
   474     lua_pushvalue(L, -1);
   475     lua_rawget(L, json_path_shadowtbl_idx);
   476     if (lua_isnil(L, -1)) {
   477       // if no shadow table is found,
   478       if (lua_type(L, -1) == LUA_TTABLE) {
   479         // and if "current value" is a table,
   480         // drop nil from stack:
   481         lua_pop(L, 1);
   482         // get "next value" using the "path key":
   483         lua_pushvalue(L, idx++);
   484         lua_gettable(L, -2);
   485       } else {
   486         // if "current value" is not a table,
   487         // then the path cannot be walked and nil (already on top of stack) is returned:
   488         return 1;
   489       }
   490     } else {
   491       // if a shadow table is found,
   492       // set "current value" to its shadow table:
   493       lua_replace(L, -2);
   494       // get "next value" using the "path key":
   495       lua_pushvalue(L, idx++);
   496       lua_rawget(L, -2);
   497     }
   498     // the "next value" replaces the "current value":
   499     lua_replace(L, -2);
   500   }
   501   if (!type_mode) {
   502     // if a value (and not its type) was requested,
   503     // check if value is the null-marker, and store nil on top of Lua stack in that case:
   504     if (lua_rawequal(L, -1, json_path_nullmark_idx)) lua_pushnil(L);
   505   } else {
   506     // if the type was requested,
   507     // check if value is the null-marker:
   508     if (lua_rawequal(L, -1, json_path_nullmark_idx)) {
   509       // if yes, store string "null" on top of Lua stack:
   510       lua_pushliteral(L, "null");
   511     } else {
   512       // otherwise,
   513       // check if metatable indicates "object" or "array":
   514       if (lua_getmetatable(L, -1)) {
   515         json_regfetch(L, objectmt);
   516         if (lua_rawequal(L, -2, -1)) {
   517           // if value has metatable for JSON objects,
   518           // return string "object":
   519           lua_pushliteral(L, "object");
   520           return 1;
   521         }
   522         json_regfetch(L, arraymt);
   523         if (lua_rawequal(L, -3, -1)) {
   524           // if value has metatable for JSON arrays,
   525           // return string "object":
   526           lua_pushliteral(L, "array");
   527           return 1;
   528         }
   529         // remove 3 metatables (one of the value, two for comparison) from stack:
   530         lua_pop(L, 3);
   531       }
   532       // otherwise, get the Lua type:
   533       lua_pushstring(L, lua_typename(L, lua_type(L, -1)));
   534     }
   535   }
   536   // return the top most value on the Lua stack:
   537   return 1;
   538 }
   540 // gets a value from a JSON document (passed as first argument)
   541 // using a path (passed as variable number of keys after first argument):
   542 static int json_get(lua_State *L) {
   543   return json_path(L, 0);
   544 }
   546 // gets a value's type from a JSON document (passed as first argument)
   547 // using a path (variable number of keys after first argument):
   548 static int json_type(lua_State *L) {
   549   return json_path(L, 1);
   550 }
   552 // checks if a value in a JSON document (first argument) is
   553 // explicitly set to null:
   554 static int json_isnull(lua_State *L) {
   555   const char *jsontype;
   556   // call json_type function with variable arguments:
   557   lua_pushcfunction(L, json_type);
   558   lua_insert(L, 1);
   559   lua_call(L, lua_gettop(L) - 1, 1);
   560   // return true if result equals to string "null", otherwise return false:
   561   jsontype = lua_tostring(L, -1);
   562   if (jsontype && !strcmp(jsontype, "null")) lua_pushboolean(L, 1);
   563   else lua_pushboolean(L, 0);
   564   return 1;
   565 }
   567 // special Lua stack indicies for json_setnull function:
   568 #define json_setnull_unknownmt_idx 3
   569 #define json_setnull_objectmt_idx 4
   570 #define json_setnull_arraymt_idx 5
   571 #define json_setnull_shadowtbl_idx 6
   573 // sets a value in a JSON object or JSON array explicitly to null:
   574 // NOTE: JSON null is different than absence of a key
   575 static int json_setnull(lua_State *L) {
   576   // stack shall contain two function arguments:
   577   lua_settop(L, 2);
   578   // push unknownmt onto stack position 3:
   579   json_regfetch(L, unknownmt);
   580   // push objectmt onto stack position 4:
   581   json_regfetch(L, objectmt);
   582   // push arraymt onto stack position 5:
   583   json_regfetch(L, arraymt);
   584   // push shadowtbl onto stack position 6:
   585   json_regfetch(L, shadowtbl);
   586   // set metatable if necessary (leaves unknown number of elements on stack):
   587   if (
   588     !lua_getmetatable(L, 1) || (
   589       !lua_rawequal(L, -1, json_setnull_unknownmt_idx) &&
   590       !lua_rawequal(L, -1, json_setnull_objectmt_idx) &&
   591       !lua_rawequal(L, -1, json_setnull_arraymt_idx)
   592     )
   593   ) {
   594     lua_pushvalue(L, json_setnull_unknownmt_idx);
   595     lua_setmetatable(L, 1);
   596   }
   597   // try to get shadow table:
   598   lua_pushvalue(L, 1);
   599   lua_rawget(L, json_setnull_shadowtbl_idx);
   600   if (lua_isnil(L, -1)) {
   601     // if no shadow table is found,
   602     // create new shadow table (and leave it on top of stack):
   603     lua_newtable(L);
   604     // register shadow table:
   605     lua_pushvalue(L, 1);
   606     lua_pushvalue(L, -2);
   607     lua_rawset(L, json_setnull_shadowtbl_idx);
   608   }
   609   // push key (second argument) and null-marker after shadow table onto stack:
   610   lua_pushvalue(L, 2);
   611   json_pushlightref(L, nullmark);
   612   // store key and null-marker in shadow table:
   613   lua_rawset(L, -3);
   614   // return nothing:
   615   return 0;
   616 }
   618 // returns the length of a JSON array (or zero for a table without numeric keys):
   619 static int json_len(lua_State *L) {
   620   // stack shall contain one function argument:
   621   lua_settop(L, 1);
   622   // try to get corresponding shadow table for first argument:
   623   json_regfetch(L, shadowtbl);
   624   lua_pushvalue(L, 1);
   625   lua_rawget(L, -2);
   626   // if shadow table does not exist, return length of argument, else length of shadow table:
   627   lua_pushnumber(L, lua_rawlen(L, lua_isnil(L, -1) ? 1 : -1));
   628   return 1;
   629 }
   631 // special Lua stack indicies for json_index function:
   632 #define json_index_nullmark_idx 3
   633 #define json_index_shadowtbl_idx 4
   635 static int json_index(lua_State *L) {
   636   // stack shall contain two function arguments:
   637   lua_settop(L, 2);
   638   // push nullmark onto stack position 3:
   639   json_pushlightref(L, nullmark);
   640   // push shadowtbl onto stack position 4:
   641   json_regfetch(L, shadowtbl);
   642   // get corresponding shadow table for first argument:
   643   lua_pushvalue(L, 1);
   644   lua_rawget(L, json_index_shadowtbl_idx);
   645   // throw error if no shadow table was found:
   646   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
   647   // use key passed as second argument to lookup value in shadow table:
   648   lua_pushvalue(L, 2);
   649   lua_rawget(L, -2);
   650   // if value is null-marker, then push nil onto stack:
   651   if (lua_rawequal(L, -1, json_index_nullmark_idx)) lua_pushnil(L);
   652   // return either looked up value, or nil
   653   return 1;
   654 }
   656 static int json_newindex(lua_State *L) {
   657   // stack shall contain three function arguments:
   658   lua_settop(L, 3);
   659   // get corresponding shadow table for first argument:
   660   json_regfetch(L, shadowtbl);
   661   lua_pushvalue(L, 1);
   662   lua_rawget(L, -2);
   663   // throw error if no shadow table was found:
   664   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
   665   // replace first argument with shadow table:
   666   lua_replace(L, 1);
   667   // reset stack and use second and third argument to write to shadow table:
   668   lua_settop(L, 3);
   669   lua_rawset(L, 1);
   670   // return nothing:
   671   return 0;
   672 }
   674 // special Lua stack indicies for json_pairs_iterfunc function:
   675 #define json_pairs_iterfunc_nullmark_idx 3
   676 #define json_pairs_iterfunc_shadowtbl_idx 4
   678 static int json_pairs_iterfunc(lua_State *L) {
   679   // stack shall contain two function arguments:
   680   lua_settop(L, 2);
   681   // push nullmark onto stack position 3:
   682   json_pushlightref(L, nullmark);
   683   // push shadowtbl onto stack position 4:
   684   json_regfetch(L, shadowtbl);
   685   // get corresponding shadow table for first argument:
   686   lua_pushvalue(L, 1);
   687   lua_rawget(L, json_pairs_iterfunc_shadowtbl_idx);
   688   // throw error if no shadow table was found:
   689   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
   690   // get next key value pair from shadow table (using previous key from argument 2)
   691   // and return nothing if there is no next pair:
   692   lua_pushvalue(L, 2);
   693   if (!lua_next(L, -2)) return 0;
   694   // replace null-marker with nil:
   695   if (lua_rawequal(L, -1, json_pairs_iterfunc_nullmark_idx)) {
   696     lua_pop(L, 1);
   697     lua_pushnil(L);
   698   }
   699   // return key and value (or key and nil, if null-marker was found):
   700   return 2;
   701 }
   703 // returns a triple such that 'for key, value in pairs(obj) do ... end'
   704 // iterates through all key value pairs (including JSON null keys represented as Lua nil):
   705 static int json_pairs(lua_State *L) {
   706   // return triple of function json_pairs_iterfunc, first argument, and nil:
   707   lua_pushcfunction(L, json_pairs_iterfunc);
   708   lua_pushvalue(L, 1);
   709   lua_pushnil(L);
   710   return 3;
   711 }
   713 // special Lua stack indicies for json_ipairs_iterfunc function:
   714 #define json_ipairs_iterfunc_nullmark_idx 3
   715 #define json_ipairs_iterfunc_shadowtbl_idx 4
   717 static int json_ipairs_iterfunc(lua_State *L) {
   718   lua_Integer idx;
   719   // stack shall contain two function arguments:
   720   lua_settop(L, 2);
   721   // push nullmark onto stack position 3:
   722   json_pushlightref(L, nullmark);
   723   // push shadowtbl onto stack position 4:
   724   json_regfetch(L, shadowtbl);
   725   // calculate new index by incrementing second argument:
   726   idx = lua_tointeger(L, 2) + 1;
   727   // get corresponding shadow table for first argument:
   728   lua_pushvalue(L, 1);
   729   lua_rawget(L, json_ipairs_iterfunc_shadowtbl_idx);
   730   // throw error if no shadow table was found:
   731   if (lua_isnil(L, -1)) return luaL_error(L, "Shadow table not found");
   732   // do integer lookup in shadow table:
   733   lua_rawgeti(L, -1, idx);
   734   // return nothing if there was no value:
   735   if (lua_isnil(L, -1)) return 0;
   736   // return new index and
   737   // either the looked up value if it is not equal to the null-marker
   738   // or nil instead of null-marker:
   739   lua_pushinteger(L, idx);
   740   if (lua_rawequal(L, -2, json_ipairs_iterfunc_nullmark_idx)) lua_pushnil(L);
   741   else lua_pushvalue(L, -2);
   742   return 2;
   743 }
   745 // returns a triple such that 'for idx, value in ipairs(ary) do ... end'
   746 // iterates through all values (including JSON null represented as Lua nil):
   747 static int json_ipairs(lua_State *L) {
   748   // return triple of function json_ipairs_iterfunc, first argument, and zero:
   749   lua_pushcfunction(L, json_ipairs_iterfunc);
   750   lua_pushvalue(L, 1);
   751   lua_pushinteger(L, 0);
   752   return 3;
   753 }
   755 #define JSON_TABLETYPE_UNKNOWN 0
   756 #define JSON_TABLETYPE_OBJECT 1
   757 #define JSON_TABLETYPE_ARRAY 2
   759 static int json_export(lua_State *L) {
   760   lua_Number num;
   761   const char *str;
   762   unsigned char c;
   763   size_t strlen;
   764   size_t pos = 0;
   765   luaL_Buffer buf;
   766   char hexcode[7];  // backslash, character 'u', 4 hex digits, and terminating NULL byte
   767   int luatype;
   768   int tabletype = JSON_TABLETYPE_UNKNOWN;
   769   int needsep = 0;
   770   lua_Integer idx;
   771   lua_settop(L, 1);
   772   switch (lua_type(L, 1)) {
   773   case LUA_TNIL:
   774     lua_pushliteral(L, "null");
   775     return 1;
   776   case LUA_TNUMBER:
   777     num = lua_tonumber(L, 1);
   778     if (isnan(num)) return luaL_error(L, "JSON export not possible for NaN value");
   779     if (isinf(num)) return luaL_error(L, "JSON export not possible for infinite numbers");
   780     lua_tostring(L, 1);
   781     return 1;
   782   case LUA_TBOOLEAN:
   783     if (lua_toboolean(L, 1)) lua_pushliteral(L, "true");
   784     else lua_pushliteral(L, "false");
   785     return 1;
   786   case LUA_TSTRING:
   787     str = lua_tolstring(L, 1, &strlen);
   788     luaL_buffinit(L, &buf);
   789     luaL_addchar(&buf, '"');
   790     while (pos < strlen) {
   791       c = str[pos++];
   792       if (c == '"')       luaL_addstring(&buf, "\\\"");
   793       else if (c == '\\') luaL_addstring(&buf, "\\\\");
   794       else if (c == 127)  luaL_addstring(&buf, "\\u007F");
   795       else if (c >= 32)   luaL_addchar(&buf, c);
   796       else if (c == '\b') luaL_addstring(&buf, "\\b");
   797       else if (c == '\f') luaL_addstring(&buf, "\\f");
   798       else if (c == '\n') luaL_addstring(&buf, "\\n");
   799       else if (c == '\r') luaL_addstring(&buf, "\\r");
   800       else if (c == '\t') luaL_addstring(&buf, "\\t");
   801       else if (c == '\v') luaL_addstring(&buf, "\\v");
   802       else {
   803         sprintf(hexcode, "\\u%04X", c);
   804         luaL_addstring(&buf, hexcode);
   805       }
   806     }
   807     luaL_addchar(&buf, '"');
   808     luaL_pushresult(&buf);
   809     return 1;
   810   case LUA_TTABLE:
   811     if (lua_getmetatable(L, 1)) {
   812       json_regfetch(L, objectmt);
   813       if (lua_rawequal(L, -2, -1)) {
   814         tabletype = JSON_TABLETYPE_OBJECT;
   815       } else {
   816         json_regfetch(L, arraymt);
   817         if (lua_rawequal(L, -3, -1)) tabletype = JSON_TABLETYPE_ARRAY;
   818       }
   819     }
   820     json_regfetch(L, shadowtbl);
   821     lua_pushvalue(L, 1);
   822     lua_rawget(L, -2);
   823     if (!lua_isnil(L, -1)) lua_replace(L, 1);
   824     lua_settop(L, 1);
   825     if (tabletype == JSON_TABLETYPE_UNKNOWN) {
   826       for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
   827         luatype = lua_type(L, -2);
   828         if (tabletype == JSON_TABLETYPE_UNKNOWN) {
   829           if (luatype == LUA_TSTRING) tabletype = JSON_TABLETYPE_OBJECT;
   830           else if (luatype == LUA_TNUMBER) tabletype = JSON_TABLETYPE_ARRAY;
   831         } else if (
   832           (tabletype == JSON_TABLETYPE_OBJECT && luatype == LUA_TNUMBER) ||
   833           (tabletype == JSON_TABLETYPE_ARRAY && luatype == LUA_TSTRING)
   834         ) {
   835           goto json_export_tabletype_error;
   836         }
   837       }
   838     }
   839     switch (tabletype) {
   840     case JSON_TABLETYPE_OBJECT:
   841       lua_settop(L, 3);
   842       luaL_buffinit(L, &buf);
   843       luaL_addchar(&buf, '{');
   844       for (lua_pushnil(L); lua_next(L, 1); ) {
   845         if (lua_type(L, -2) == LUA_TSTRING) {
   846           lua_replace(L, 3);
   847           lua_replace(L, 2);
   848           if (needsep) luaL_addchar(&buf, ',');
   849           else needsep = 1;
   850           lua_pushcfunction(L, json_export);
   851           lua_pushvalue(L, 2);
   852           lua_call(L, 1, 1);
   853           luaL_addvalue(&buf);
   854           luaL_addchar(&buf, ':');
   855           if (json_islightref(L, 3, nullmark)) {
   856             luaL_addstring(&buf, "null");
   857           } else {
   858             lua_pushcfunction(L, json_export);
   859             lua_pushvalue(L, 3);
   860             lua_call(L, 1, 1);
   861             luaL_addvalue(&buf);
   862           }
   863           lua_pushvalue(L, 2);
   864         } else {
   865           lua_pop(L, 1);
   866         }
   867       }
   868       luaL_addchar(&buf, '}');
   869       luaL_pushresult(&buf);
   870       return 1;
   871     case JSON_TABLETYPE_ARRAY:
   872       lua_settop(L, 2);
   873       luaL_buffinit(L, &buf);
   874       luaL_addchar(&buf, '[');
   875       for (idx = 1; ; idx++) {
   876         lua_rawgeti(L, 1, idx);
   877         if (lua_isnil(L, -1)) {
   878           lua_pop(L, 1);
   879           break;
   880         }
   881         lua_replace(L, 2);
   882         if (needsep) luaL_addchar(&buf, ',');
   883         else needsep = 1;
   884         lua_pushcfunction(L, json_export);
   885         lua_pushvalue(L, 2);
   886         lua_call(L, 1, 1);
   887         luaL_addvalue(&buf);
   888       }
   889       luaL_addchar(&buf, ']');
   890       luaL_pushresult(&buf);
   891       return 1;
   892     }
   893     json_export_tabletype_error:
   894     return luaL_error(L, "JSON export not possible for ambiguous table (cannot decide whether it is an object or array)");
   895   }
   896   return luaL_error(L, "JSON export not possible for values of type \"%s\"", lua_typename(L, lua_type(L, 1)));
   897 }
   899 // functions in library module:
   900 static const struct luaL_Reg json_module_functions[] = {
   901   {"object", json_object},
   902   {"array", json_array},
   903   {"import", json_import},
   904   {"export", json_export},
   905   {"get", json_get},
   906   {"type", json_type},
   907   {"isnull", json_isnull},
   908   {"setnull", json_setnull},
   909   {NULL, NULL}
   910 };
   912 // metamethods for JSON objects, JSON arrays, and unknown JSON collections (object or array):
   913 static const struct luaL_Reg json_metatable_functions[] = {
   914   {"__len", json_len},
   915   {"__index", json_index},
   916   {"__newindex", json_newindex},
   917   {"__pairs", json_pairs},
   918   {"__ipairs", json_ipairs},
   919   {NULL, NULL}
   920 };
   922 // initializes json library:
   923 int luaopen_json(lua_State *L) {
   924   // empty stack:
   925   lua_settop(L, 0);
   926   // push library module onto stack position 1:
   927   lua_newtable(L);
   928   // register library functions:
   929   luaL_setfuncs(L, json_module_functions, 0);
   930   // create and store unknownmt:
   931   lua_newtable(L);
   932   luaL_setfuncs(L, json_metatable_functions, 0);
   933   json_regstore(L, unknownmt);
   934   // create and store objectmt:
   935   lua_newtable(L);
   936   luaL_setfuncs(L, json_metatable_functions, 0);
   937   json_regstore(L, objectmt);
   938   // create and store arraymt:
   939   lua_newtable(L);
   940   luaL_setfuncs(L, json_metatable_functions, 0);
   941   json_regstore(L, arraymt);
   942   // create and store ephemeron table to store shadow tables for each JSON object/array
   943   // to allow NULL values returned as nil
   944   lua_newtable(L);
   945   lua_newtable(L);  // metatable for ephemeron table
   946   lua_pushliteral(L, "__mode");
   947   lua_pushliteral(L, "k");
   948   lua_rawset(L, -3);
   949   lua_setmetatable(L, -2);
   950   json_regstore(L, shadowtbl);
   951   // return library module stored on lowest stack position:
   952   lua_settop(L, 1);
   953   return 1;
   954 }
