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