From cdc12b239d4332a896d933cfa998fdc40b6a7aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Diot?= Date: Wed, 12 Jun 2024 12:15:28 +0200 Subject: [PATCH] Squashed 'src/deps/src/lua-cjson/' changes from 881accc8fa..f95cd9ea1e f95cd9ea1e doc: add comment for dtoa.c. c92ecda533 feature: Lua 5.3 + 5.4 integer support, with CI and conflicts fixed. d20576d5ce bugfix: bus error or SIGSEGV caused by encode not keep buffer. 2bfad8f5ee Bugfix: Lua cjson integer overflow issues (CVE-2022-24834) (#94) git-subtree-dir: src/deps/src/lua-cjson git-subtree-split: f95cd9ea1e39221a36818772eb85f05b4164bbb1 --- .github/workflows/test.yml | 4 +- .gitignore | 1 + .travis.yml | 2 +- CMakeLists.txt | 7 ++ Makefile | 8 +- build-packages.sh | 2 +- dtoa.c | 115 ++++++++++++----- fpconv.c | 2 +- ....rockspec => lua-cjson-2.1.0.14-1.rockspec | 4 +- lua-cjson.spec | 2 +- lua_cjson.c | 117 +++++++++++++----- manual.txt => manual.adoc | 13 +- performance.txt => performance.adoc | 2 +- strbuf.c | 116 +++++------------ strbuf.h | 46 +++---- tests/agentzh.t | 42 +++++++ tests/genutf8.pl | 1 + 17 files changed, 299 insertions(+), 185 deletions(-) rename lua-cjson-2.1.0.12-1.rockspec => lua-cjson-2.1.0.14-1.rockspec (97%) rename manual.txt => manual.adoc (98%) rename performance.txt => performance.adoc (98%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 74fdf1f6f..5567f6370 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: runtestArgs: "LUA_INCLUDE_DIR=.lua/include/luajit-2.1" runtestEnv: "SKIP_CMAKE=1" - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@master @@ -47,7 +47,7 @@ jobs: sudo cpanm --notest Test::Base Test::LongString - name: cppcheck - run: cppcheck -i .lua/ -i .install/ -i dtoa.c --force --error-exitcode=1 --enable=warning . + run: cppcheck -i .lua/ -i .install/ -i dtoa.c --force --error-exitcode=1 --enable=warning --inline-suppr . - name: prove run: LUA_BIN=lua prove -Itests tests diff --git a/.gitignore b/.gitignore index b7678892e..d9b1095f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.html *.o *.so +*.a notes packages tags diff --git a/.travis.yml b/.travis.yml index 091ff98b2..469d5dd5a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ env: - JOBS=3 - LUAROCKS_VER=2.4.2 matrix: - #- LUA=1 LUA_DIR=/usr LUA_INCLUDE_DIR=$LUA_DIR/include/lua5.1 + #- LUA=1 LUA_DIR=/usr LUA_INCLUDE_DIR=$LUA_DIR/include/lua5.1 - LUAJIT=1 LUA_DIR=/usr/local LUA_INCLUDE_DIR=$LUA_DIR/include/luajit-2.1 LUA_SUFFIX=--lua-suffix=jit install: diff --git a/CMakeLists.txt b/CMakeLists.txt index f7eaf8985..8ac6d3814 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,13 @@ else() set(_lua_module_dir "${_lua_lib_dir}/lua/5.1") endif() +if(MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + add_definitions(-Dinline=__inline) + add_definitions(-Dsnprintf=_snprintf) + add_definitions(-Dstrncasecmp=_strnicmp) +endif() + add_library(cjson MODULE lua_cjson.c strbuf.c ${FPCONV_SOURCES}) set_target_properties(cjson PROPERTIES PREFIX "") target_link_libraries(cjson ${_MODULE_LINK}) diff --git a/Makefile b/Makefile index 55c214278..c5966c5d8 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,8 @@ LUA_CMODULE_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION) LUA_MODULE_DIR ?= $(PREFIX)/share/lua/$(LUA_VERSION) LUA_BIN_DIR ?= $(PREFIX)/bin +AR= $(CC) -o + ##### Platform overrides ##### ## ## Tweak one of the platform sections below to suit your situation. @@ -84,12 +86,12 @@ OBJS = lua_cjson.o strbuf.o $(FPCONV_OBJS) .PHONY: all clean install install-extra doc -.SUFFIXES: .html .txt +.SUFFIXES: .html .adoc .c.o: $(CC) -c $(CFLAGS) $(CPPFLAGS) $(BUILD_CFLAGS) -o $@ $< -.txt.html: +.adoc.html: $(ASCIIDOC) -n -a toc $< all: $(TARGET) @@ -97,7 +99,7 @@ all: $(TARGET) doc: manual.html performance.html $(TARGET): $(OBJS) - $(CC) $(LDFLAGS) $(CJSON_LDFLAGS) -o $@ $(OBJS) + $(AR) $@ $(LDFLAGS) $(CJSON_LDFLAGS) $(OBJS) install: $(TARGET) mkdir -p $(DESTDIR)$(LUA_CMODULE_DIR) diff --git a/build-packages.sh b/build-packages.sh index 23a6f2ed5..2c45e6605 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -5,7 +5,7 @@ # Build packages. Use current checked out version, or a specific tag/commit. # Files requiring a version bump -VERSION_FILES="lua-cjson-2.1devel-1.rockspec lua-cjson.spec lua_cjson.c manual.txt runtests.sh tests/test.lua" +VERSION_FILES="lua-cjson-2.1devel-1.rockspec lua-cjson.spec lua_cjson.c manual.adoc runtests.sh tests/test.lua" [ "$1" ] && BRANCH="$1" || BRANCH="`git describe --match '[1-3].[0-9]*'`" VERSION="`git describe --match '[1-3].[0-9]*' $BRANCH`" diff --git a/dtoa.c b/dtoa.c index 8dac7b8de..19a0caef9 100644 --- a/dtoa.c +++ b/dtoa.c @@ -1,3 +1,7 @@ +/* The code comes from https://portal.ampl.com/~dmg/netlib/fp/dtoa.c + * Go to https://portal.ampl.com/~dmg/netlib/fp/changes for the detailed changes. + */ + /**************************************************************** * * The author of this software is David M. Gay. @@ -1533,12 +1537,18 @@ ThInfo { set_max_dtoa_threads(unsigned int n) { size_t L; + ThInfo *newTI1; if (n > maxthreads) { L = n*sizeof(ThInfo); if (TI1) { - TI1 = (ThInfo*)REALLOC(TI1, L); - memset(TI1 + maxthreads, 0, (n-maxthreads)*sizeof(ThInfo)); + newTI1 = (ThInfo*)REALLOC(TI1, L); + if (newTI1) { + TI1 = newTI1; + memset(TI1 + maxthreads, 0, (n-maxthreads)*sizeof(ThInfo)); + } + else + return; } else { TI1 = (ThInfo*)MALLOC(L); @@ -1871,7 +1881,7 @@ mult(Bigint *a, Bigint *b MTd) #else #ifdef Pack_32 for(; xb < xbe; xb++, xc0++) { - if (y = *xb & 0xffff) { + if ((y = *xb & 0xffff)) { x = xa; xc = xc0; carry = 0; @@ -1885,7 +1895,7 @@ mult(Bigint *a, Bigint *b MTd) while(x < xae); *xc = carry; } - if (y = *xb >> 16) { + if ((y = *xb >> 16)) { x = xa; xc = xc0; carry = 0; @@ -2718,13 +2728,14 @@ enum { /* rounding values: same as FLT_ROUNDS */ }; void -gethex( const char **sp, U *rvp, int rounding, int sign MTd) +gethex(const char **sp, U *rvp, int rounding, int sign MTd) { Bigint *b; + char d; const unsigned char *decpt, *s0, *s, *s1; Long e, e1; ULong L, lostbits, *x; - int big, denorm, esign, havedig, k, n, nbits, up, zret; + int big, denorm, esign, havedig, k, n, nb, nbits, nz, up, zret; #ifdef IBM int j; #endif @@ -2742,6 +2753,9 @@ gethex( const char **sp, U *rvp, int rounding, int sign MTd) #endif #endif /*}}*/ }; +#ifdef IEEE_Arith + int check_denorm = 0; +#endif #ifdef USE_LOCALE int i; #ifdef NO_LOCALE_CACHE @@ -2893,7 +2907,7 @@ gethex( const char **sp, U *rvp, int rounding, int sign MTd) k++; b = Balloc(k MTa); x = b->x; - n = 0; + havedig = n = nz = 0; L = 0; #ifdef USE_LOCALE for(i = 0; decimalpoint[i+1]; ++i); @@ -2908,22 +2922,28 @@ gethex( const char **sp, U *rvp, int rounding, int sign MTd) if (*--s1 == '.') continue; #endif + if ((d = hexdig[*s1])) + havedig = 1; + else if (!havedig) { + e += 4; + continue; + } if (n == ULbits) { *x++ = L; L = 0; n = 0; } - L |= (hexdig[*s1] & 0x0f) << n; + L |= (d & 0x0f) << n; n += 4; } *x++ = L; b->wds = n = x - b->x; - n = ULbits*n - hi0bits(L); + nb = ULbits*n - hi0bits(L); nbits = Nbits; lostbits = 0; x = b->x; - if (n > nbits) { - n -= nbits; + if (nb > nbits) { + n = nb - nbits; if (any_on(b,n)) { lostbits = 1; k = n - 1; @@ -2936,8 +2956,8 @@ gethex( const char **sp, U *rvp, int rounding, int sign MTd) rshift(b, n); e += n; } - else if (n < nbits) { - n = nbits - n; + else if (nb < nbits) { + n = nbits - nb; b = lshift(b, n MTa); e -= n; x = b->x; @@ -2992,12 +3012,49 @@ gethex( const char **sp, U *rvp, int rounding, int sign MTd) return; } k = n - 1; +#ifdef IEEE_Arith + if (!k) { + switch(rounding) { + case Round_near: + if (((b->x[0] & 3) == 3) || (lostbits && (b->x[0] & 1))) { + multadd(b, 1, 1 MTa); + emin_check: + if (b->x[1] == (1 << (Exp_shift + 1))) { + rshift(b,1); + e = emin; + goto normal; + } + } + break; + case Round_up: + if (!sign && (lostbits || (b->x[0] & 1))) { + incr_denorm: + multadd(b, 1, 2 MTa); + check_denorm = 1; + lostbits = 0; + goto emin_check; + } + break; + case Round_down: + if (sign && (lostbits || (b->x[0] & 1))) + goto incr_denorm; + break; + } + } +#endif if (lostbits) lostbits = 1; else if (k > 0) lostbits = any_on(b,k); +#ifdef IEEE_Arith + else if (check_denorm) + goto no_lostbits; +#endif if (x[k>>kshift] & 1 << (k & kmask)) lostbits |= 2; +#ifdef IEEE_Arith + no_lostbits: +#endif nbits -= n; rshift(b,n); e = emin; @@ -3022,16 +3079,9 @@ gethex( const char **sp, U *rvp, int rounding, int sign MTd) k = b->wds; b = increment(b MTa); x = b->x; - if (denorm) { -#if 0 - if (nbits == Nbits - 1 - && x[nbits >> kshift] & 1 << (nbits & kmask)) - denorm = 0; /* not currently used */ -#endif - } - else if (b->wds > k + if (!denorm && (b->wds > k || ((n = nbits & kmask) !=0 - && hi0bits(x[k-1]) < 32-n)) { + && hi0bits(x[k-1]) < 32-n))) { rshift(b,1); if (++e > Emax) goto ovfl; @@ -3041,8 +3091,10 @@ gethex( const char **sp, U *rvp, int rounding, int sign MTd) #ifdef IEEE_Arith if (denorm) word0(rvp) = b->wds > 1 ? b->x[1] & ~0x100000 : 0; - else + else { + normal: word0(rvp) = (b->x[1] & ~0x100000) | ((e + 0x3ff + 52) << 20); + } word1(rvp) = b->x[0]; #endif #ifdef IBM @@ -3409,6 +3461,7 @@ retlow1: if ((j = ((word0(rv) & Exp_mask) >> Exp_shift) - bc->scale) <= 0) { i = 1 - j; if (i <= 31) { + /* cppcheck-suppress integerOverflowCond */ if (word1(rv) & (0x1 << i)) goto odd; } @@ -3619,10 +3672,11 @@ fpconv_strtod(const char *s00, char **se) c = *++s; if (c > '0' && c <= '9') { L = c - '0'; - s1 = s; - while((c = *++s) >= '0' && c <= '9') - L = 10*L + c - '0'; - if (s - s1 > 8 || L > 19999) + while((c = *++s) >= '0' && c <= '9') { + if (L <= 19999) + L = 10*L + c - '0'; + } + if (L > 19999) /* Avoid confusion from exponents * so large that e might overflow. */ @@ -4884,6 +4938,7 @@ nrv_alloc(const char *s, char *s0, size_t s0len, char **rve, int n MTd) s0 = rv_alloc(n MTa); else if (s0len <= n) { rv = 0; + /* cppcheck-suppress nullPointerArithmetic */ t = rv + n; goto rve_chk; } @@ -5237,9 +5292,11 @@ dtoa_r(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve, char #ifndef SET_INEXACT #ifdef Check_FLT_ROUNDS try_quick = Rounding == 1; +#else + try_quick = 1; #endif #endif /*SET_INEXACT*/ -#endif +#endif /*USE_BF96*/ if (mode > 5) { mode -= 4; @@ -5281,6 +5338,7 @@ dtoa_r(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve, char else if (blen <= i) { buf = 0; if (rve) + /* cppcheck-suppress nullPointerArithmetic */ *rve = buf + i; return buf; } @@ -5469,6 +5527,7 @@ dtoa_r(double dd, int mode, int ndigits, int *decpt, int *sign, char **rve, char res3 = p10->b1 * dbhi + (tv3 & 0xffffffffull); res = p10->b0 * dbhi + (tv3>>32) + (res3>>32); be += p10->e - 0x3fe; + /* cppcheck-suppress integerOverflowCond */ eulp = j1 = be - 54 + ulpadj; if (!(res & 0x8000000000000000ull)) { --be; diff --git a/fpconv.c b/fpconv.c index 0ef7f18cb..64208f8dc 100644 --- a/fpconv.c +++ b/fpconv.c @@ -130,7 +130,7 @@ double fpconv_strtod(const char *nptr, char **endptr) /* Duplicate number into buffer */ if (buflen >= FPCONV_G_FMT_BUFSIZE) { /* Handle unusually large numbers */ - buf = malloc(buflen + 1); + buf = (char *)malloc(buflen + 1); if (!buf) { fprintf(stderr, "Out of memory"); abort(); diff --git a/lua-cjson-2.1.0.12-1.rockspec b/lua-cjson-2.1.0.14-1.rockspec similarity index 97% rename from lua-cjson-2.1.0.12-1.rockspec rename to lua-cjson-2.1.0.14-1.rockspec index 9ed4272ef..4376a081f 100644 --- a/lua-cjson-2.1.0.12-1.rockspec +++ b/lua-cjson-2.1.0.14-1.rockspec @@ -1,9 +1,9 @@ package = "lua-cjson" -version = "2.1.0.11-1" +version = "2.1.0.14-1" source = { url = "git+https://github.com/openresty/lua-cjson", - tag = "2.1.0.11", + tag = "2.1.0.14", } description = { diff --git a/lua-cjson.spec b/lua-cjson.spec index 13fc56d2e..3d3cb16fd 100644 --- a/lua-cjson.spec +++ b/lua-cjson.spec @@ -50,7 +50,7 @@ rm -rf "$RPM_BUILD_ROOT" %files %defattr(-,root,root,-) -%doc LICENSE NEWS performance.html performance.txt manual.html manual.txt rfc4627.txt THANKS +%doc LICENSE NEWS performance.html performance.adoc manual.html manual.adoc rfc4627.txt THANKS %{lualibdir}/* %{luadatadir}/* %{_bindir}/* diff --git a/lua_cjson.c b/lua_cjson.c index 42672de32..bbd8eff86 100644 --- a/lua_cjson.c +++ b/lua_cjson.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,13 @@ #endif +#ifdef _MSC_VER +#define CJSON_EXPORT __declspec(dllexport) +#define strncasecmp(x,y,z) _strnicmp(x,y,z) +#else +#define CJSON_EXPORT extern +#endif + /* Workaround for Solaris platforms missing isinf() */ #if !defined(isinf) && (defined(USE_INTERNAL_ISINF) || defined(MISSING_ISINF)) #define isinf(x) (!isnan(x) && isnan((x) - (x))) @@ -103,8 +111,8 @@ #define json_lightudata_mask(ludata) (ludata) #endif -#if LUA_VERSION_NUM > 501 -#define lua_objlen(L,i) lua_rawlen(L, (i)) +#if LUA_VERSION_NUM >= 502 +#define lua_objlen(L,i) luaL_len(L, (i)) #endif static const char * const *json_empty_array; @@ -117,6 +125,7 @@ typedef enum { T_ARR_END, T_STRING, T_NUMBER, + T_INTEGER, T_BOOLEAN, T_NULL, T_COLON, @@ -134,6 +143,7 @@ static const char *json_token_type_name[] = { "T_ARR_END", "T_STRING", "T_NUMBER", + "T_INTEGER", "T_BOOLEAN", "T_NULL", "T_COLON", @@ -179,13 +189,14 @@ typedef struct { typedef struct { json_token_type_t type; - int index; + size_t index; union { const char *string; double number; + lua_Integer integer; int boolean; } value; - int string_len; + size_t string_len; } json_token_t; static const char *char2escape[256] = { @@ -233,7 +244,7 @@ static json_config_t *json_fetch_config(lua_State *l) { json_config_t *cfg; - cfg = lua_touserdata(l, lua_upvalueindex(1)); + cfg = (json_config_t *)lua_touserdata(l, lua_upvalueindex(1)); if (!cfg) luaL_error(l, "BUG: Unable to fetch CJSON configuration"); @@ -442,7 +453,7 @@ static int json_destroy_config(lua_State *l) { json_config_t *cfg; - cfg = lua_touserdata(l, 1); + cfg = (json_config_t *)lua_touserdata(l, 1); if (cfg) strbuf_free(&cfg->encode_buf); cfg = NULL; @@ -455,7 +466,11 @@ static void json_create_config(lua_State *l) json_config_t *cfg; int i; - cfg = lua_newuserdata(l, sizeof(*cfg)); + cfg = (json_config_t *)lua_newuserdata(l, sizeof(*cfg)); + if (!cfg) + abort(); + + memset(cfg, 0, sizeof(*cfg)); /* Create GC method to clean up strbuf */ lua_newtable(l); @@ -547,9 +562,9 @@ static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *js static void json_append_string(lua_State *l, strbuf_t *json, int lindex) { const char *escstr; - unsigned i; const char *str; size_t len; + size_t i; str = lua_tolstring(l, lindex, &len); @@ -557,6 +572,8 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex) * This buffer is reused constantly for small strings * If there are any excess pages, they won't be hit anyway. * This gains ~5% speedup. */ + if (len > SIZE_MAX / 6 - 3) + abort(); /* Overflow check */ strbuf_ensure_empty_length(json, len * 6 + 2); strbuf_append_char_unsafe(json, '\"'); @@ -646,9 +663,9 @@ static int json_append_data(lua_State *l, json_config_t *cfg, /* json_append_array args: * - lua_State * - JSON strbuf - * - Size of passwd Lua array (top of stack) */ + * - Size of passed Lua array (top of stack) */ static void json_append_array(lua_State *l, json_config_t *cfg, int current_depth, - strbuf_t *json, int array_length) + strbuf_t *json, int array_length, int raw) { int comma, i, json_pos, err; @@ -660,7 +677,17 @@ static void json_append_array(lua_State *l, json_config_t *cfg, int current_dept if (comma++ > 0) strbuf_append_char(json, ','); - lua_rawgeti(l, -1, i); + if (raw) { + lua_rawgeti(l, -1, i); + } else { +#if LUA_VERSION_NUM >= 503 + lua_geti(l, -1, i); +#else + lua_pushinteger(l, i); + lua_gettable(l, -2); +#endif + } + err = json_append_data(l, cfg, current_depth, json); if (err) { strbuf_set_length(json, json_pos); @@ -677,8 +704,17 @@ static void json_append_array(lua_State *l, json_config_t *cfg, int current_dept static void json_append_number(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex) { - double num = lua_tonumber(l, lindex); int len; +#if LUA_VERSION_NUM >= 503 + if (lua_isinteger(l, lindex)) { + lua_Integer num = lua_tointeger(l, lindex); + strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE); /* max length of int64 is 19 */ + len = sprintf(strbuf_empty_ptr(json), LUA_INTEGER_FMT, num); + strbuf_extend_length(json, len); + return; + } +#endif + double num = lua_tonumber(l, lindex); if (cfg->encode_invalid_numbers == 0) { /* Prevent encoding invalid numbers */ @@ -766,6 +802,7 @@ static int json_append_data(lua_State *l, json_config_t *cfg, int len; int as_array = 0; int has_metatable; + int raw = 1; switch (lua_type(l, -1)) { case LUA_TSTRING: @@ -790,17 +827,30 @@ static int json_append_data(lua_State *l, json_config_t *cfg, lua_pushlightuserdata(l, json_lightudata_mask(&json_array)); lua_rawget(l, LUA_REGISTRYINDEX); as_array = lua_rawequal(l, -1, -2); - lua_pop(l, 2); + if (as_array) { + raw = 1; + lua_pop(l, 2); + len = lua_objlen(l, -1); + } else { + raw = 0; + lua_pop(l, 2); + if (luaL_getmetafield(l, -1, "__len")) { + lua_pushvalue(l, -2); + lua_call(l, 1, 1); + len = lua_tonumber(l, -1); + lua_pop(l, 1); + as_array = 1; + } + } } if (as_array) { - len = lua_objlen(l, -1); - json_append_array(l, cfg, current_depth, json, len); + json_append_array(l, cfg, current_depth, json, len, raw); } else { len = lua_array_length(l, cfg, json); if (len > 0 || (len == 0 && !cfg->encode_empty_table_as_object)) { - json_append_array(l, cfg, current_depth, json, len); + json_append_array(l, cfg, current_depth, json, len, raw); } else { if (has_metatable) { lua_getmetatable(l, -1); @@ -810,7 +860,9 @@ static int json_append_data(lua_State *l, json_config_t *cfg, as_array = lua_rawequal(l, -1, -2); lua_pop(l, 2); /* pop pointer + metatable */ if (as_array) { - json_append_array(l, cfg, current_depth, json, 0); + len = lua_objlen(l, -1); + raw = 1; + json_append_array(l, cfg, current_depth, json, len, raw); break; } } @@ -825,7 +877,7 @@ static int json_append_data(lua_State *l, json_config_t *cfg, if (lua_touserdata(l, -1) == NULL) { strbuf_append_mem(json, "null", 4); } else if (lua_touserdata(l, -1) == json_lightudata_mask(&json_array)) { - json_append_array(l, cfg, current_depth, json, 0); + json_append_array(l, cfg, current_depth, json, 0, 1); } break; default: @@ -848,7 +900,7 @@ static int json_encode(lua_State *l) strbuf_t local_encode_buf; strbuf_t *encode_buf; char *json; - int len; + size_t len; luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); @@ -1138,13 +1190,19 @@ static int json_is_invalid_number(json_parse_t *json) static void json_next_number_token(json_parse_t *json, json_token_t *token) { char *endptr; - - token->type = T_NUMBER; - token->value.number = fpconv_strtod(json->ptr, &endptr); - if (json->ptr == endptr) - json_set_token_error(token, json, "invalid number"); - else - json->ptr = endptr; /* Skip the processed number */ + token->value.integer = strtoll(json->ptr, &endptr, 10); + if (json->ptr == endptr || *endptr == '.' || *endptr == 'e' || + *endptr == 'E' || *endptr == 'x') { + token->type = T_NUMBER; + token->value.number = fpconv_strtod(json->ptr, &endptr); + if (json->ptr == endptr) { + json_set_token_error(token, json, "invalid number"); + return; + } + } else { + token->type = T_INTEGER; + } + json->ptr = endptr; /* Skip the processed number */ return; } @@ -1380,6 +1438,9 @@ static void json_process_value(lua_State *l, json_parse_t *json, case T_NUMBER: lua_pushnumber(l, token->value.number); break;; + case T_INTEGER: + lua_pushinteger(l, token->value.integer); + break;; case T_BOOLEAN: lua_pushboolean(l, token->value.boolean); break;; @@ -1595,7 +1656,7 @@ static int lua_cjson_safe_new(lua_State *l) return 1; } -int luaopen_cjson(lua_State *l) +CJSON_EXPORT int luaopen_cjson(lua_State *l) { lua_cjson_new(l); @@ -1609,7 +1670,7 @@ int luaopen_cjson(lua_State *l) return 1; } -int luaopen_cjson_safe(lua_State *l) +CJSON_EXPORT int luaopen_cjson_safe(lua_State *l) { lua_cjson_safe_new(l); diff --git a/manual.txt b/manual.adoc similarity index 98% rename from manual.txt rename to manual.adoc index a12e3785e..83303a306 100644 --- a/manual.txt +++ b/manual.adoc @@ -1,6 +1,6 @@ = Lua CJSON 2.1devel Manual = Mark Pulford -:revdate: 1st March 2012 +:revdate: August 2016 Overview -------- @@ -20,7 +20,7 @@ The Lua CJSON module provides JSON support for Lua. Lua CJSON is covered by the MIT license. Review the file +LICENSE+ for details. -The latest version of this software is available from the +The current stable version of this software is available from the http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CJSON website]. Feel free to email me if you have any patches, suggestions, or comments. @@ -29,8 +29,8 @@ Feel free to email me if you have any patches, suggestions, or comments. Installation ------------ -Lua CJSON requires either http://www.lua.org[Lua] 5.1, Lua 5.2, or -http://www.luajit.org[LuaJIT] to build. +Lua CJSON requires either http://www.lua.org[Lua] 5.1, Lua 5.2, Lua 5.3, +or http://www.luajit.org[LuaJIT] to build. The build method can be selected from 4 options: @@ -203,8 +203,8 @@ Import Lua CJSON via the Lua +require+ function. Lua CJSON does not register a global module table. The +cjson+ module will throw an error during JSON conversion if any -invalid data is encountered. Refer to <> -and <> for details. +invalid data is encountered. Refer to <> and +<> for details. The +cjson.safe+ module behaves identically to the +cjson+ module, except when errors are encountered during JSON conversion. On error, the @@ -238,6 +238,7 @@ workaround if required. Lua CJSON should be reinitialised via different locale per thread is not supported. +[[decode]] decode ~~~~~~ diff --git a/performance.txt b/performance.adoc similarity index 98% rename from performance.txt rename to performance.adoc index fc3a5bb5d..a36412d69 100644 --- a/performance.txt +++ b/performance.adoc @@ -26,7 +26,7 @@ http://chiselapp.com/user/dhkolf/repository/dkjson/[DKJSON 2.1]:: https://github.com/brimworks/lua-yajl[Lua YAJL 2.0]:: - C wrapper for the YAJL library -http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CSJON 2.0.0]:: +http://www.kyne.com.au/%7Emark/software/lua-cjson.php[Lua CJSON 2.0.0]:: - C implementation with no dependencies on other libraries diff --git a/strbuf.c b/strbuf.c index ed1336785..73cd9b895 100644 --- a/strbuf.c +++ b/strbuf.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "strbuf.h" @@ -38,38 +39,38 @@ static void die(const char *fmt, ...) va_end(arg); fprintf(stderr, "\n"); - exit(-1); + abort(); } -void strbuf_init(strbuf_t *s, int len) +void strbuf_init(strbuf_t *s, size_t len) { - int size; + size_t size; - if (len <= 0) + if (!len) size = STRBUF_DEFAULT_SIZE; else - size = len + 1; /* \0 terminator */ - + size = len + 1; + if (size < len) + die("Overflow, len: %zu", len); s->buf = NULL; s->size = size; s->length = 0; - s->increment = STRBUF_DEFAULT_INCREMENT; s->dynamic = 0; s->reallocs = 0; s->debug = 0; - s->buf = malloc(size); + s->buf = (char *)malloc(size); if (!s->buf) die("Out of memory"); strbuf_ensure_null(s); } -strbuf_t *strbuf_new(int len) +strbuf_t *strbuf_new(size_t len) { strbuf_t *s; - s = malloc(sizeof(strbuf_t)); + s = (strbuf_t*)malloc(sizeof(strbuf_t)); if (!s) die("Out of memory"); @@ -81,20 +82,10 @@ strbuf_t *strbuf_new(int len) return s; } -void strbuf_set_increment(strbuf_t *s, int increment) -{ - /* Increment > 0: Linear buffer growth rate - * Increment < -1: Exponential buffer growth rate */ - if (increment == 0 || increment == -1) - die("BUG: Invalid string increment"); - - s->increment = increment; -} - static inline void debug_stats(strbuf_t *s) { if (s->debug) { - fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n", + fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %zd, size: %zd\n", (long)s, s->reallocs, s->length, s->size); } } @@ -113,7 +104,7 @@ void strbuf_free(strbuf_t *s) free(s); } -char *strbuf_free_to_string(strbuf_t *s, int *len) +char *strbuf_free_to_string(strbuf_t *s, size_t *len) { char *buf; @@ -131,57 +122,63 @@ char *strbuf_free_to_string(strbuf_t *s, int *len) return buf; } -static int calculate_new_size(strbuf_t *s, int len) +static size_t calculate_new_size(strbuf_t *s, size_t len) { - int reqsize, newsize; + size_t reqsize, newsize; if (len <= 0) die("BUG: Invalid strbuf length requested"); /* Ensure there is room for optional NULL termination */ reqsize = len + 1; + if (reqsize < len) + die("Overflow, len: %zu", len); /* If the user has requested to shrink the buffer, do it exactly */ if (s->size > reqsize) return reqsize; newsize = s->size; - if (s->increment < 0) { + if (reqsize >= SIZE_MAX / 2) { + newsize = reqsize; + } else { /* Exponential sizing */ while (newsize < reqsize) - newsize *= -s->increment; - } else if (s->increment != 0) { - /* Linear sizing */ - newsize = ((newsize + s->increment - 1) / s->increment) * s->increment; + newsize *= 2; } + if (newsize < reqsize) + die("BUG: strbuf length would overflow, len: %zu", len); + + return newsize; } /* Ensure strbuf can handle a string length bytes long (ignoring NULL * optional termination). */ -void strbuf_resize(strbuf_t *s, int len) +void strbuf_resize(strbuf_t *s, size_t len) { - int newsize; + size_t newsize; newsize = calculate_new_size(s, len); if (s->debug > 1) { - fprintf(stderr, "strbuf(%lx) resize: %d => %d\n", + fprintf(stderr, "strbuf(%lx) resize: %zd => %zd\n", (long)s, s->size, newsize); } s->size = newsize; - s->buf = realloc(s->buf, s->size); + s->buf = (char *)realloc(s->buf, s->size); if (!s->buf) - die("Out of memory"); + die("Out of memory, len: %zu", len); s->reallocs++; } void strbuf_append_string(strbuf_t *s, const char *str) { - int space, i; + int i; + size_t space; space = strbuf_empty_length(s); @@ -197,55 +194,6 @@ void strbuf_append_string(strbuf_t *s, const char *str) } } -/* strbuf_append_fmt() should only be used when an upper bound - * is known for the output string. */ -void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...) -{ - va_list arg; - int fmt_len; - - strbuf_ensure_empty_length(s, len); - - va_start(arg, fmt); - fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg); - va_end(arg); - - if (fmt_len < 0) - die("BUG: Unable to convert number"); /* This should never happen.. */ - - s->length += fmt_len; -} - -/* strbuf_append_fmt_retry() can be used when the there is no known - * upper bound for the output string. */ -void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...) -{ - va_list arg; - int fmt_len, try; - int empty_len; - - /* If the first attempt to append fails, resize the buffer appropriately - * and try again */ - for (try = 0; ; try++) { - va_start(arg, fmt); - /* Append the new formatted string */ - /* fmt_len is the length of the string required, excluding the - * trailing NULL */ - empty_len = strbuf_empty_length(s); - /* Add 1 since there is also space to store the terminating NULL. */ - fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg); - va_end(arg); - - if (fmt_len <= empty_len) - break; /* SUCCESS */ - if (try > 0) - die("BUG: length of formatted string changed"); - - strbuf_resize(s, s->length + fmt_len); - } - - s->length += fmt_len; -} /* vi:ai et sw=4 ts=4: */ diff --git a/strbuf.h b/strbuf.h index a98ee220b..d77e0f4b1 100644 --- a/strbuf.h +++ b/strbuf.h @@ -32,15 +32,13 @@ /* Size: Total bytes allocated to *buf * Length: String length, excluding optional NULL terminator. - * Increment: Allocation increments when resizing the string buffer. * Dynamic: True if created via strbuf_new() */ typedef struct { char *buf; - int size; - int length; - int increment; + size_t size; + size_t length; int dynamic; int reallocs; int debug; @@ -49,33 +47,27 @@ typedef struct { #ifndef STRBUF_DEFAULT_SIZE #define STRBUF_DEFAULT_SIZE 1023 #endif -#ifndef STRBUF_DEFAULT_INCREMENT -#define STRBUF_DEFAULT_INCREMENT -2 -#endif /* Initialise */ -extern strbuf_t *strbuf_new(int len); -extern void strbuf_init(strbuf_t *s, int len); -extern void strbuf_set_increment(strbuf_t *s, int increment); +extern strbuf_t *strbuf_new(size_t len); +extern void strbuf_init(strbuf_t *s, size_t len); /* Release */ extern void strbuf_free(strbuf_t *s); -extern char *strbuf_free_to_string(strbuf_t *s, int *len); +extern char *strbuf_free_to_string(strbuf_t *s, size_t *len); /* Management */ -extern void strbuf_resize(strbuf_t *s, int len); -static int strbuf_empty_length(strbuf_t *s); -static int strbuf_length(strbuf_t *s); -static char *strbuf_string(strbuf_t *s, int *len); -static void strbuf_ensure_empty_length(strbuf_t *s, int len); +extern void strbuf_resize(strbuf_t *s, size_t len); +static size_t strbuf_empty_length(strbuf_t *s); +static size_t strbuf_length(strbuf_t *s); +static char *strbuf_string(strbuf_t *s, size_t *len); +static void strbuf_ensure_empty_length(strbuf_t *s, size_t len); static char *strbuf_empty_ptr(strbuf_t *s); -static void strbuf_extend_length(strbuf_t *s, int len); +static void strbuf_extend_length(strbuf_t *s, size_t len); static void strbuf_set_length(strbuf_t *s, int len); /* Update */ -extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); -extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...); -static void strbuf_append_mem(strbuf_t *s, const char *c, int len); +static void strbuf_append_mem(strbuf_t *s, const char *c, size_t len); extern void strbuf_append_string(strbuf_t *s, const char *str); static void strbuf_append_char(strbuf_t *s, const char c); static void strbuf_ensure_null(strbuf_t *s); @@ -93,12 +85,12 @@ static inline int strbuf_allocated(strbuf_t *s) /* Return bytes remaining in the string buffer * Ensure there is space for a NULL terminator. */ -static inline int strbuf_empty_length(strbuf_t *s) +static inline size_t strbuf_empty_length(strbuf_t *s) { return s->size - s->length - 1; } -static inline void strbuf_ensure_empty_length(strbuf_t *s, int len) +static inline void strbuf_ensure_empty_length(strbuf_t *s, size_t len) { if (len > strbuf_empty_length(s)) strbuf_resize(s, s->length + len); @@ -114,12 +106,12 @@ static inline void strbuf_set_length(strbuf_t *s, int len) s->length = len; } -static inline void strbuf_extend_length(strbuf_t *s, int len) +static inline void strbuf_extend_length(strbuf_t *s, size_t len) { s->length += len; } -static inline int strbuf_length(strbuf_t *s) +static inline size_t strbuf_length(strbuf_t *s) { return s->length; } @@ -135,14 +127,14 @@ static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c) s->buf[s->length++] = c; } -static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len) +static inline void strbuf_append_mem(strbuf_t *s, const char *c, size_t len) { strbuf_ensure_empty_length(s, len); memcpy(s->buf + s->length, c, len); s->length += len; } -static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len) +static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, size_t len) { memcpy(s->buf + s->length, c, len); s->length += len; @@ -153,7 +145,7 @@ static inline void strbuf_ensure_null(strbuf_t *s) s->buf[s->length] = 0; } -static inline char *strbuf_string(strbuf_t *s, int *len) +static inline char *strbuf_string(strbuf_t *s, size_t *len) { if (len) *len = s->length; diff --git a/tests/agentzh.t b/tests/agentzh.t index 552630aba..88a94b821 100644 --- a/tests/agentzh.t +++ b/tests/agentzh.t @@ -332,3 +332,45 @@ Cannot serialise function: type not supported {"valid":"valid"} ["one","two","three"] + + + +=== TEST 23: array-like proxy object with __len and __index +--- lua +local cjson = require "cjson" +local real_array = {"foo", "bar", "baz"} +local proxy_array = {} +setmetatable(proxy_array, { + __len = function() return 3 end, + __index = function(t, k) + return real_array[k] + end, +}) + +print(cjson.encode(proxy_array)) +--- out +["foo","bar","baz"] + + + +=== TEST 24: check that integers are handled correctly on Lua 5.3+ +--- lua +local lv = tonumber((_VERSION):match("Lua 5%.([0-9]+)")) + +if lv >= 3 then + local cjson = require "cjson" + local array = cjson.decode [[ [10, 10.0, 3.5] ]] + for i = 1, 4 do + print(tostring(i) .. ": " .. tostring(math.type(array[i]))) + end +else + print("1: integer") + print("2: float") + print("3: float") + print("4: nil") +end +--- out +1: integer +2: float +3: float +4: nil diff --git a/tests/genutf8.pl b/tests/genutf8.pl index db661a19d..c79f238b4 100755 --- a/tests/genutf8.pl +++ b/tests/genutf8.pl @@ -6,6 +6,7 @@ # cff03b039d850f370a7362f3313e5268 use strict; +no warnings 'nonchar'; # 0xD800 - 0xDFFF are used to encode supplementary codepoints # 0x10000 - 0x10FFFF are supplementary codepoints