diff options
Diffstat (limited to '')
| -rw-r--r-- | lua_cjson.c | 92 |
1 files changed, 50 insertions, 42 deletions
diff --git a/lua_cjson.c b/lua_cjson.c index 2cef3a8..cab1922 100644 --- a/lua_cjson.c +++ b/lua_cjson.c | |||
| @@ -45,7 +45,6 @@ | |||
| 45 | 45 | ||
| 46 | #include "strbuf.h" | 46 | #include "strbuf.h" |
| 47 | 47 | ||
| 48 | #define CJSON_CONFIG_KEY "cjson_configdata" | ||
| 49 | #define DEFAULT_SPARSE_RATIO 2 | 48 | #define DEFAULT_SPARSE_RATIO 2 |
| 50 | #define DEFAULT_MAX_DEPTH 20 | 49 | #define DEFAULT_MAX_DEPTH 20 |
| 51 | 50 | ||
| @@ -87,6 +86,7 @@ static const char *json_token_type_name[] = { | |||
| 87 | typedef struct { | 86 | typedef struct { |
| 88 | json_token_type_t ch2token[256]; | 87 | json_token_type_t ch2token[256]; |
| 89 | char ch2escape[256]; | 88 | char ch2escape[256]; |
| 89 | strbuf_t encode_buf; | ||
| 90 | int sparse_ratio; | 90 | int sparse_ratio; |
| 91 | int max_depth; | 91 | int max_depth; |
| 92 | int current_depth; | 92 | int current_depth; |
| @@ -113,11 +113,14 @@ typedef struct { | |||
| 113 | 113 | ||
| 114 | /* ===== CONFIGURATION ===== */ | 114 | /* ===== CONFIGURATION ===== */ |
| 115 | 115 | ||
| 116 | static int json_config_key; | ||
| 117 | |||
| 116 | static json_config_t *json_fetch_config(lua_State *l) | 118 | static json_config_t *json_fetch_config(lua_State *l) |
| 117 | { | 119 | { |
| 118 | json_config_t *cfg; | 120 | json_config_t *cfg; |
| 119 | 121 | ||
| 120 | lua_getfield(l, LUA_REGISTRYINDEX, CJSON_CONFIG_KEY); | 122 | lua_pushlightuserdata(l, &json_config_key); |
| 123 | lua_gettable(l, LUA_REGISTRYINDEX); | ||
| 121 | cfg = lua_touserdata(l, -1); | 124 | cfg = lua_touserdata(l, -1); |
| 122 | if (!cfg) | 125 | if (!cfg) |
| 123 | luaL_error(l, "BUG: Unable to fetch cjson configuration"); | 126 | luaL_error(l, "BUG: Unable to fetch cjson configuration"); |
| @@ -244,15 +247,14 @@ static void json_create_config(lua_State *l) | |||
| 244 | cfg->sparse_ratio = DEFAULT_SPARSE_RATIO; | 247 | cfg->sparse_ratio = DEFAULT_SPARSE_RATIO; |
| 245 | cfg->max_depth = DEFAULT_MAX_DEPTH; | 248 | cfg->max_depth = DEFAULT_MAX_DEPTH; |
| 246 | cfg->strict_numbers = 1; | 249 | cfg->strict_numbers = 1; |
| 250 | |||
| 251 | strbuf_init(&cfg->encode_buf, 0); | ||
| 247 | } | 252 | } |
| 248 | 253 | ||
| 249 | /* ===== ENCODING ===== */ | 254 | /* ===== ENCODING ===== */ |
| 250 | 255 | ||
| 251 | static void json_encode_exception(lua_State *l, strbuf_t *json, int lindex, | 256 | static void json_encode_exception(lua_State *l, int lindex, const char *reason) |
| 252 | const char *reason) | ||
| 253 | { | 257 | { |
| 254 | strbuf_free(json); | ||
| 255 | |||
| 256 | luaL_error(l, "Cannot serialise %s: %s", | 258 | luaL_error(l, "Cannot serialise %s: %s", |
| 257 | lua_typename(l, lua_type(l, lindex)), reason); | 259 | lua_typename(l, lua_type(l, lindex)), reason); |
| 258 | } | 260 | } |
| @@ -290,7 +292,7 @@ static inline char *json_escape_char(int c) | |||
| 290 | * Returns nothing. Doesn't remove string from Lua stack */ | 292 | * Returns nothing. Doesn't remove string from Lua stack */ |
| 291 | static void json_append_string(lua_State *l, strbuf_t *json, int lindex) | 293 | static void json_append_string(lua_State *l, strbuf_t *json, int lindex) |
| 292 | { | 294 | { |
| 293 | char *p; | 295 | const char *p; |
| 294 | int i; | 296 | int i; |
| 295 | const char *str; | 297 | const char *str; |
| 296 | size_t len; | 298 | size_t len; |
| @@ -301,9 +303,9 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex) | |||
| 301 | * This buffer is reused constantly for small strings | 303 | * This buffer is reused constantly for small strings |
| 302 | * If there are any excess pages, they won't be hit anyway. | 304 | * If there are any excess pages, they won't be hit anyway. |
| 303 | * This gains ~5% speedup. */ | 305 | * This gains ~5% speedup. */ |
| 304 | strbuf_ensure_empty_length(json, len * 6); | 306 | strbuf_ensure_empty_length(json, len * 6 + 2); |
| 305 | 307 | ||
| 306 | strbuf_append_char(json, '\"'); | 308 | strbuf_append_char_unsafe(json, '\"'); |
| 307 | for (i = 0; i < len; i++) { | 309 | for (i = 0; i < len; i++) { |
| 308 | p = json_escape_char(str[i]); | 310 | p = json_escape_char(str[i]); |
| 309 | if (p) | 311 | if (p) |
| @@ -311,7 +313,7 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex) | |||
| 311 | else | 313 | else |
| 312 | strbuf_append_char_unsafe(json, str[i]); | 314 | strbuf_append_char_unsafe(json, str[i]); |
| 313 | } | 315 | } |
| 314 | strbuf_append_char(json, '\"'); | 316 | strbuf_append_char_unsafe(json, '\"'); |
| 315 | } | 317 | } |
| 316 | 318 | ||
| 317 | /* Find the size of the array on the top of the Lua stack | 319 | /* Find the size of the array on the top of the Lua stack |
| @@ -355,13 +357,11 @@ static int lua_array_length(lua_State *l, int sparse_ratio) | |||
| 355 | return max; | 357 | return max; |
| 356 | } | 358 | } |
| 357 | 359 | ||
| 358 | static void json_encode_descend(lua_State *l, json_config_t *cfg, | 360 | static void json_encode_descend(lua_State *l, json_config_t *cfg) |
| 359 | strbuf_t *json) | ||
| 360 | { | 361 | { |
| 361 | cfg->current_depth++; | 362 | cfg->current_depth++; |
| 362 | 363 | ||
| 363 | if (cfg->current_depth > cfg->max_depth) { | 364 | if (cfg->current_depth > cfg->max_depth) { |
| 364 | strbuf_free(json); | ||
| 365 | luaL_error(l, "Cannot serialise, excessive nesting (%d)", | 365 | luaL_error(l, "Cannot serialise, excessive nesting (%d)", |
| 366 | cfg->current_depth); | 366 | cfg->current_depth); |
| 367 | } | 367 | } |
| @@ -378,14 +378,14 @@ static void json_append_array(lua_State *l, json_config_t *cfg, strbuf_t *json, | |||
| 378 | { | 378 | { |
| 379 | int comma, i; | 379 | int comma, i; |
| 380 | 380 | ||
| 381 | json_encode_descend(l, cfg, json); | 381 | json_encode_descend(l, cfg); |
| 382 | 382 | ||
| 383 | strbuf_append_string(json, "[ "); | 383 | strbuf_append_mem(json, "[ ", 2); |
| 384 | 384 | ||
| 385 | comma = 0; | 385 | comma = 0; |
| 386 | for (i = 1; i <= array_length; i++) { | 386 | for (i = 1; i <= array_length; i++) { |
| 387 | if (comma) | 387 | if (comma) |
| 388 | strbuf_append_string(json, ", "); | 388 | strbuf_append_mem(json, ", ", 2); |
| 389 | else | 389 | else |
| 390 | comma = 1; | 390 | comma = 1; |
| 391 | 391 | ||
| @@ -394,7 +394,7 @@ static void json_append_array(lua_State *l, json_config_t *cfg, strbuf_t *json, | |||
| 394 | lua_pop(l, 1); | 394 | lua_pop(l, 1); |
| 395 | } | 395 | } |
| 396 | 396 | ||
| 397 | strbuf_append_string(json, " ]"); | 397 | strbuf_append_mem(json, " ]", 2); |
| 398 | 398 | ||
| 399 | cfg->current_depth--; | 399 | cfg->current_depth--; |
| 400 | } | 400 | } |
| @@ -405,9 +405,9 @@ static void json_append_number(lua_State *l, strbuf_t *json, int index, | |||
| 405 | double num = lua_tonumber(l, index); | 405 | double num = lua_tonumber(l, index); |
| 406 | 406 | ||
| 407 | if (strict && (isinf(num) || isnan(num))) | 407 | if (strict && (isinf(num) || isnan(num))) |
| 408 | json_encode_exception(l, json, index, "must not be NaN or Inf"); | 408 | json_encode_exception(l, index, "must not be NaN or Inf"); |
| 409 | 409 | ||
| 410 | strbuf_append_fmt(json, LUA_NUMBER_FMT, num); | 410 | strbuf_append_number(json, num); |
| 411 | } | 411 | } |
| 412 | 412 | ||
| 413 | static void json_append_object(lua_State *l, json_config_t *cfg, | 413 | static void json_append_object(lua_State *l, json_config_t *cfg, |
| @@ -415,17 +415,17 @@ static void json_append_object(lua_State *l, json_config_t *cfg, | |||
| 415 | { | 415 | { |
| 416 | int comma, keytype; | 416 | int comma, keytype; |
| 417 | 417 | ||
| 418 | json_encode_descend(l, cfg, json); | 418 | json_encode_descend(l, cfg); |
| 419 | 419 | ||
| 420 | /* Object */ | 420 | /* Object */ |
| 421 | strbuf_append_string(json, "{ "); | 421 | strbuf_append_mem(json, "{ ", 2); |
| 422 | 422 | ||
| 423 | lua_pushnil(l); | 423 | lua_pushnil(l); |
| 424 | /* table, startkey */ | 424 | /* table, startkey */ |
| 425 | comma = 0; | 425 | comma = 0; |
| 426 | while (lua_next(l, -2) != 0) { | 426 | while (lua_next(l, -2) != 0) { |
| 427 | if (comma) | 427 | if (comma) |
| 428 | strbuf_append_string(json, ", "); | 428 | strbuf_append_mem(json, ", ", 2); |
| 429 | else | 429 | else |
| 430 | comma = 1; | 430 | comma = 1; |
| 431 | 431 | ||
| @@ -436,12 +436,12 @@ static void json_append_object(lua_State *l, json_config_t *cfg, | |||
| 436 | * convert the value in the callers data structure. */ | 436 | * convert the value in the callers data structure. */ |
| 437 | strbuf_append_char(json, '"'); | 437 | strbuf_append_char(json, '"'); |
| 438 | json_append_number(l, json, -2, cfg->strict_numbers); | 438 | json_append_number(l, json, -2, cfg->strict_numbers); |
| 439 | strbuf_append_string(json, "\": "); | 439 | strbuf_append_mem(json, "\": ", 3); |
| 440 | } else if (keytype == LUA_TSTRING) { | 440 | } else if (keytype == LUA_TSTRING) { |
| 441 | json_append_string(l, json, -2); | 441 | json_append_string(l, json, -2); |
| 442 | strbuf_append_string(json, ": "); | 442 | strbuf_append_mem(json, ": ", 2); |
| 443 | } else { | 443 | } else { |
| 444 | json_encode_exception(l, json, -2, | 444 | json_encode_exception(l, -2, |
| 445 | "table key must be a number or string"); | 445 | "table key must be a number or string"); |
| 446 | /* never returns */ | 446 | /* never returns */ |
| 447 | } | 447 | } |
| @@ -452,7 +452,7 @@ static void json_append_object(lua_State *l, json_config_t *cfg, | |||
| 452 | /* table, key */ | 452 | /* table, key */ |
| 453 | } | 453 | } |
| 454 | 454 | ||
| 455 | strbuf_append_string(json, " }"); | 455 | strbuf_append_mem(json, " }", 2); |
| 456 | 456 | ||
| 457 | cfg->current_depth--; | 457 | cfg->current_depth--; |
| 458 | } | 458 | } |
| @@ -471,9 +471,9 @@ static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json) | |||
| 471 | break; | 471 | break; |
| 472 | case LUA_TBOOLEAN: | 472 | case LUA_TBOOLEAN: |
| 473 | if (lua_toboolean(l, -1)) | 473 | if (lua_toboolean(l, -1)) |
| 474 | strbuf_append_string(json, "true"); | 474 | strbuf_append_mem(json, "true", 4); |
| 475 | else | 475 | else |
| 476 | strbuf_append_string(json, "false"); | 476 | strbuf_append_mem(json, "false", 5); |
| 477 | break; | 477 | break; |
| 478 | case LUA_TTABLE: | 478 | case LUA_TTABLE: |
| 479 | len = lua_array_length(l, cfg->sparse_ratio); | 479 | len = lua_array_length(l, cfg->sparse_ratio); |
| @@ -483,17 +483,17 @@ static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json) | |||
| 483 | json_append_object(l, cfg, json); | 483 | json_append_object(l, cfg, json); |
| 484 | break; | 484 | break; |
| 485 | case LUA_TNIL: | 485 | case LUA_TNIL: |
| 486 | strbuf_append_string(json, "null"); | 486 | strbuf_append_mem(json, "null", 4); |
| 487 | break; | 487 | break; |
| 488 | case LUA_TLIGHTUSERDATA: | 488 | case LUA_TLIGHTUSERDATA: |
| 489 | if (lua_touserdata(l, -1) == NULL) { | 489 | if (lua_touserdata(l, -1) == NULL) { |
| 490 | strbuf_append_string(json, "null"); | 490 | strbuf_append_mem(json, "null", 4); |
| 491 | break; | 491 | break; |
| 492 | } | 492 | } |
| 493 | default: | 493 | default: |
| 494 | /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, | 494 | /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, |
| 495 | * and LUA_TLIGHTUSERDATA) cannot be serialised */ | 495 | * and LUA_TLIGHTUSERDATA) cannot be serialised */ |
| 496 | json_encode_exception(l, json, -1, "type not supported"); | 496 | json_encode_exception(l, -1, "type not supported"); |
| 497 | /* never returns */ | 497 | /* never returns */ |
| 498 | } | 498 | } |
| 499 | } | 499 | } |
| @@ -501,7 +501,6 @@ static void json_append_data(lua_State *l, json_config_t *cfg, strbuf_t *json) | |||
| 501 | static int json_encode(lua_State *l) | 501 | static int json_encode(lua_State *l) |
| 502 | { | 502 | { |
| 503 | json_config_t *cfg; | 503 | json_config_t *cfg; |
| 504 | strbuf_t buf; | ||
| 505 | char *json; | 504 | char *json; |
| 506 | int len; | 505 | int len; |
| 507 | 506 | ||
| @@ -510,12 +509,13 @@ static int json_encode(lua_State *l) | |||
| 510 | cfg = json_fetch_config(l); | 509 | cfg = json_fetch_config(l); |
| 511 | cfg->current_depth = 0; | 510 | cfg->current_depth = 0; |
| 512 | 511 | ||
| 513 | strbuf_init(&buf, 0); | 512 | /* Reset persistent encode_buf. Avoids temporary allocation |
| 514 | json_append_data(l, cfg, &buf); | 513 | * for a single call. */ |
| 515 | json = strbuf_free_to_string(&buf, &len); | 514 | cfg->encode_buf.length = 0; |
| 515 | json_append_data(l, cfg, &cfg->encode_buf); | ||
| 516 | json = strbuf_string(&cfg->encode_buf, &len); | ||
| 516 | 517 | ||
| 517 | lua_pushlstring(l, json, len); | 518 | lua_pushlstring(l, json, len); |
| 518 | free(json); | ||
| 519 | 519 | ||
| 520 | return 1; | 520 | return 1; |
| 521 | } | 521 | } |
| @@ -761,8 +761,9 @@ static void json_next_token(json_parse_t *json, json_token_t *token) | |||
| 761 | return; | 761 | return; |
| 762 | } | 762 | } |
| 763 | 763 | ||
| 764 | if (token->type == T_END) | 764 | if (token->type == T_END) { |
| 765 | return; | 765 | return; |
| 766 | } | ||
| 766 | 767 | ||
| 767 | /* Found a known single character token, advance index and return */ | 768 | /* Found a known single character token, advance index and return */ |
| 768 | if (token->type != T_UNKNOWN) { | 769 | if (token->type != T_UNKNOWN) { |
| @@ -851,8 +852,9 @@ static void json_parse_object_context(lua_State *l, json_parse_t *json) | |||
| 851 | json_next_token(json, &token); | 852 | json_next_token(json, &token); |
| 852 | 853 | ||
| 853 | /* Handle empty objects */ | 854 | /* Handle empty objects */ |
| 854 | if (token.type == T_OBJ_END) | 855 | if (token.type == T_OBJ_END) { |
| 855 | return; | 856 | return; |
| 857 | } | ||
| 856 | 858 | ||
| 857 | while (1) { | 859 | while (1) { |
| 858 | if (token.type != T_STRING) | 860 | if (token.type != T_STRING) |
| @@ -874,8 +876,9 @@ static void json_parse_object_context(lua_State *l, json_parse_t *json) | |||
| 874 | 876 | ||
| 875 | json_next_token(json, &token); | 877 | json_next_token(json, &token); |
| 876 | 878 | ||
| 877 | if (token.type == T_OBJ_END) | 879 | if (token.type == T_OBJ_END) { |
| 878 | return; | 880 | return; |
| 881 | } | ||
| 879 | 882 | ||
| 880 | if (token.type != T_COMMA) | 883 | if (token.type != T_COMMA) |
| 881 | json_throw_parse_error(l, json, "comma or object end", &token); | 884 | json_throw_parse_error(l, json, "comma or object end", &token); |
| @@ -899,8 +902,9 @@ static void json_parse_array_context(lua_State *l, json_parse_t *json) | |||
| 899 | json_next_token(json, &token); | 902 | json_next_token(json, &token); |
| 900 | 903 | ||
| 901 | /* Handle empty arrays */ | 904 | /* Handle empty arrays */ |
| 902 | if (token.type == T_ARR_END) | 905 | if (token.type == T_ARR_END) { |
| 903 | return; | 906 | return; |
| 907 | } | ||
| 904 | 908 | ||
| 905 | for (i = 1; ; i++) { | 909 | for (i = 1; ; i++) { |
| 906 | json_process_value(l, json, &token); | 910 | json_process_value(l, json, &token); |
| @@ -908,8 +912,9 @@ static void json_parse_array_context(lua_State *l, json_parse_t *json) | |||
| 908 | 912 | ||
| 909 | json_next_token(json, &token); | 913 | json_next_token(json, &token); |
| 910 | 914 | ||
| 911 | if (token.type == T_ARR_END) | 915 | if (token.type == T_ARR_END) { |
| 912 | return; | 916 | return; |
| 917 | } | ||
| 913 | 918 | ||
| 914 | if (token.type != T_COMMA) | 919 | if (token.type != T_COMMA) |
| 915 | json_throw_parse_error(l, json, "comma or array end", &token); | 920 | json_throw_parse_error(l, json, "comma or array end", &token); |
| @@ -995,8 +1000,11 @@ int luaopen_cjson(lua_State *l) | |||
| 995 | { NULL, NULL } | 1000 | { NULL, NULL } |
| 996 | }; | 1001 | }; |
| 997 | 1002 | ||
| 1003 | /* Use json_fetch_config as a pointer. | ||
| 1004 | * It's faster than using a config string, and more unique */ | ||
| 1005 | lua_pushlightuserdata(l, &json_config_key); | ||
| 998 | json_create_config(l); | 1006 | json_create_config(l); |
| 999 | lua_setfield(l, LUA_REGISTRYINDEX, CJSON_CONFIG_KEY); | 1007 | lua_settable(l, LUA_REGISTRYINDEX); |
| 1000 | 1008 | ||
| 1001 | luaL_register(l, "cjson", reg); | 1009 | luaL_register(l, "cjson", reg); |
| 1002 | 1010 | ||
