From 85bf3b798f6d52c374c35f7fbe47df132891d3b2 Mon Sep 17 00:00:00 2001 From: Mark Pulford Date: Fri, 30 Dec 2011 17:48:12 +1030 Subject: Add support for Lua 5.2 and cjson.new Update all Lua scripts to use new module init style everywhere: local json = require "cjson" Lua CJSON does not register a global table under Lua 5.2. The global table can be disabled under Lua 5.1 with DISABLE_CJSON_GLOBAL. Other changes: - Store CJSON configuration as an upvalue for each function. - Add "cjson.new" function to create another module table with a separate configuration. - Add _NAME and _VERSION variables. --- Makefile | 3 ++- lua_cjson.c | 73 +++++++++++++++++++++++++++++++++++++++++++------------- manual.txt | 61 +++++++++++++++++++++++++++++++++++----------- tests/bench.lua | 2 +- tests/common.lua | 6 ++--- tests/decode.lua | 4 ++-- tests/encode.lua | 4 ++-- tests/test.lua | 16 ++++++------- 8 files changed, 122 insertions(+), 47 deletions(-) diff --git a/Makefile b/Makefile index fb63d99..c9343f9 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,8 @@ LUA_VERSION = 5.1 ## Available defines for CJSON_CFLAGS # -# USE_INTERNAL_ISINF: Workaround for Solaris platforms missing isinf(). +# USE_INTERNAL_ISINF: Workaround for Solaris platforms missing isinf(). +# DISABLE_CJSON_GLOBAL: Do not store module is "cjson" global ## Build defaults PREFIX = /usr/local diff --git a/lua_cjson.c b/lua_cjson.c index 8e9b237..be5cdc2 100644 --- a/lua_cjson.c +++ b/lua_cjson.c @@ -45,6 +45,10 @@ #include "strbuf.h" #include "fpconv.h" +#ifndef CJSON_MODNAME +#define CJSON_MODNAME "cjson" +#endif + #ifndef CJSON_VERSION #define CJSON_VERSION "1.0devel" #endif @@ -175,22 +179,16 @@ static const char *char2escape[256] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; -static int json_config_key; - /* ===== CONFIGURATION ===== */ static json_config_t *json_fetch_config(lua_State *l) { json_config_t *cfg; - lua_pushlightuserdata(l, &json_config_key); - lua_gettable(l, LUA_REGISTRYINDEX); - cfg = lua_touserdata(l, -1); + cfg = lua_touserdata(l, lua_upvalueindex(1)); if (!cfg) luaL_error(l, "BUG: Unable to fetch CJSON configuration"); - lua_pop(l, 1); - return cfg; } @@ -1246,7 +1244,27 @@ static int json_decode(lua_State *l) /* ===== INITIALISATION ===== */ -int luaopen_cjson(lua_State *l) +#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 +/* Compatibility for Lua 5.1. + * + * luaL_setfuncs() is used to create a module table where the functions have + * json_config_t as their first upvalue. Code borrowed from Lua 5.2 source. */ +static void luaL_setfuncs (lua_State *l, const luaL_Reg *reg, int nup) +{ + int i; + + luaL_checkstack(l, nup, "too many upvalues"); + for (; reg->name != NULL; reg++) { /* fill the table with given functions */ + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(l, -nup); + lua_pushcclosure(l, reg->func, nup); /* closure with those upvalues */ + lua_setfield(l, -(nup + 2), reg->name); + } + lua_pop(l, nup); /* remove upvalues */ +} +#endif + +static int lua_cjson_new(lua_State *l) { luaL_Reg reg[] = { { "encode", json_encode }, @@ -1257,28 +1275,51 @@ int luaopen_cjson(lua_State *l) { "encode_keep_buffer", json_cfg_encode_keep_buffer }, { "refuse_invalid_numbers", json_cfg_refuse_invalid_numbers }, { "update_locale", json_update_locale }, + { "new", lua_cjson_new }, { NULL, NULL } }; - /* Update the current locale for g_fmt/strtod */ + /* Update the current locale for g_fmt/strtod. + * Using different locales per-thread is not supported. */ fpconv_update_locale(); - /* Use json_config_key as a pointer. - * It's faster than using a config string, and more unique */ - lua_pushlightuserdata(l, &json_config_key); - json_create_config(l); - lua_settable(l, LUA_REGISTRYINDEX); + /* cjson module table */ + lua_newtable(l); - luaL_register(l, "cjson", reg); + /* Register functions with config data as upvalue */ + json_create_config(l); + luaL_setfuncs(l, reg, 1); /* Set cjson.null */ lua_pushlightuserdata(l, NULL); lua_setfield(l, -2, "null"); - /* Set cjson.version */ + /* Set module name / version fields */ + lua_pushliteral(l, CJSON_MODNAME); + lua_setfield(l, -2, "_NAME"); + lua_pushliteral(l, CJSON_VERSION); + lua_setfield(l, -2, "_VERSION"); lua_pushliteral(l, CJSON_VERSION); lua_setfield(l, -2, "version"); + return 1; +} + +int luaopen_cjson(lua_State *l) +{ + lua_cjson_new(l); + +#if !defined(DISABLE_CJSON_GLOBAL) && (!defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502) + /* Register a global "cjson" table to maintain compatibility with earlier + * versions of Lua CJSON which used luaL_register() under Lua 5.1. + * + * From Lua 5.2 onwards, Lua CJSON does not automatically register a global + * table. + */ + lua_pushvalue(l, -1); + lua_setglobal(l, CJSON_MODNAME); +#endif + /* Return cjson table */ return 1; } diff --git a/manual.txt b/manual.txt index a3e12cf..810d3d1 100644 --- a/manual.txt +++ b/manual.txt @@ -16,8 +16,6 @@ Lua CJSON provides fast JSON parsing and encoding support for Lua. .Caveats - UTF-16 and UTF-32 are not supported. -- Multi-threading within a single Lua state is not supported - (+lua_lock+ / +lua_unlock+). Lua CJSON is covered by the MIT license. Review the file +LICENSE+ for details. @@ -32,7 +30,7 @@ comments. Installation Methods -------------------- -Lua CJSON requires either http://www.lua.org[Lua] or +Lua CJSON requires either http://www.lua.org[Lua] 5.1, Lua 5.2, or http://www.luajit.org[LuaJIT] to build. There are 4 build methods available: @@ -44,11 +42,13 @@ RPM:: Linux LuaRocks:: Unix, Windows -Build options (#define) +Build Options (#define) ~~~~~~~~~~~~~~~~~~~~~~~ [horizontal] USE_INTERNAL_ISINF:: Workaround for Solaris platforms missing isinf(). +DISABLE_CJSON_GLOBAL:: Do not store module table in global "cjson" + variable. Make @@ -125,9 +125,9 @@ Synopsis [source,lua] ------------ -require "cjson" --- Or: +-- Module initalisation methods local cjson = require "cjson" +local cjson2 = cjson.new() -- Translate Lua value to/from JSON text = cjson.encode(value) @@ -141,6 +141,34 @@ keep = cjson.encode_keep_buffer([keep]) ------------ +Module Instantiation +~~~~~~~~~~~~~~~~~~~~ + +[source,lua] +------------ +local cjson = require "cjson" +local cjson2 = cjson.new() +------------ + +Lua CJSON can be loaded via +require+. A global +cjson+ table is +registered under Lua 5.1 to maintain backward compatibility. Lua CJSON +does not register a global table under Lua 5.2 since this practice is +discouraged. + ++cjson.new+ can be used to instantiate an independent copy of the Lua +CJSON module. The new module has a separate persistent encoding +buffer, and default settings. + +Lua CJSON can support Lua implementations using multiple pre-emptive +threads within a single Lua state provided the persistent encoding +buffer is not shared. This can be achieved by one of the following +methods: + +- Disabling the persistent encoding buffer with +encode_keep_buffer+. +- Ensuring only a single thread calls +encode+ at a time. +- Using a separate +cjson+ instantiation per pre-emptive thread. + + decode ~~~~~~ @@ -423,6 +451,18 @@ Disabled for decoding:: All numbers supported by +strtod+(3) will be parsed. API (Variables) --------------- +_NAME +~~~~~ + +The name of the Lua CJSON module (+"cjson"+). + + +_VERSION +~~~~~~~~ + +The version number of the Lua CJSON module (Eg, +"1.0devel"+). + + null ~~~~ @@ -430,13 +470,6 @@ Lua CJSON decodes JSON +null+ as a Lua +lightuserdata+ NULL pointer. +cjson.null+ is provided for comparison. -version -~~~~~~~ - -The version number of the Lua CJSON module in use can be found in -+cjson.version+. - - [sect1] References ---------- @@ -445,4 +478,4 @@ References - http://www.json.org/[JSON website] -// vi:tw=70: +// vi:ft=asciidoc tw=70: diff --git a/tests/bench.lua b/tests/bench.lua index fdd0bb0..c81213d 100755 --- a/tests/bench.lua +++ b/tests/bench.lua @@ -72,7 +72,7 @@ function bench_file(filename) return benchmark(tests, 0.1, 5) end -cjson.encode_keep_buffer(true) +json.encode_keep_buffer(true) for i = 1, #arg do local results = bench_file(arg[i]) diff --git a/tests/common.lua b/tests/common.lua index f3dc6f7..7472a10 100644 --- a/tests/common.lua +++ b/tests/common.lua @@ -1,4 +1,4 @@ -require "cjson" +local json = require "cjson" -- Misc routines to assist with CJSON testing -- @@ -77,8 +77,8 @@ function serialise_value(value, indent, depth) if indent == nil then indent = "" end if depth == nil then depth = 0 end - if value == cjson.null then - return "cjson.null" + if value == json.null then + return "json.null" elseif type(value) == "string" then return string.format("%q", value) elseif type(value) == "nil" or type(value) == "number" or diff --git a/tests/decode.lua b/tests/decode.lua index cac29e6..89354cd 100755 --- a/tests/decode.lua +++ b/tests/decode.lua @@ -7,8 +7,8 @@ -- ./decode.lua test.json require "common" -require "cjson" +local json = require "cjson" local json_text = file_load(arg[1]) -local t = cjson.decode(json_text) +local t = json.decode(json_text) print(serialise_value(t)) diff --git a/tests/encode.lua b/tests/encode.lua index f13787c..a8d749a 100755 --- a/tests/encode.lua +++ b/tests/encode.lua @@ -7,7 +7,7 @@ -- ./encode.lua lua_data.lua require "common" -require "cjson" +local json = require "cjson" function get_lua_table(file) local func = loadstring("data = " .. file_load(file)) @@ -23,6 +23,6 @@ function get_lua_table(file) end local t = get_lua_table(arg[1]) -print(cjson.encode(t)) +print(json.encode(t)) -- vi:ai et sw=4 ts=4: diff --git a/tests/test.lua b/tests/test.lua index bdae6ea..99ac73a 100755 --- a/tests/test.lua +++ b/tests/test.lua @@ -112,8 +112,8 @@ local decode_numeric_tests = { local encode_table_tests = { function() - cjson.encode_sparse_array(true, 2, 3) - cjson.encode_max_depth(5) + json.encode_sparse_array(true, 2, 3) + json.encode_max_depth(5) return "Setting sparse array (true, 2, 3) / max depth (5)" end, { json.encode, { { [3] = "sparse test" } }, @@ -211,19 +211,19 @@ local escape_tests = { local locale_tests = { function () os.setlocale("cs_CZ") - cjson.update_locale() + json.update_locale() return "Setting locale to cs_CZ (comma separator)" end, { json.encode, { 1.5 }, true, { '1.5' } }, { json.decode, { "[ 10, \"test\" ]" }, true, { { 10, "test" } } }, function () os.setlocale("C") - cjson.update_locale() + json.update_locale() return "Reverting locale to POSIX" end } -print(string.format("Testing Lua CJSON version %s\n", cjson.version)) +print(string.format("Testing Lua CJSON version %s\n", json.version)) run_test_group("decode simple value", decode_simple_tests) run_test_group("encode simple value", encode_simple_tests) @@ -232,7 +232,7 @@ run_test_group("decode numeric", decode_numeric_tests) -- INCLUDE: -- - Sparse array exception.. -- - .. --- cjson.encode_sparse_array(true, 2, 3) +-- json.encode_sparse_array(true, 2, 3) run_test_group("encode table", encode_table_tests) run_test_group("decode error", decode_error_tests) @@ -240,8 +240,8 @@ run_test_group("encode error", encode_error_tests) run_test_group("escape", escape_tests) run_test_group("locale", locale_tests) -cjson.refuse_invalid_numbers(false) -cjson.encode_max_depth(20) +json.refuse_invalid_numbers(false) +json.encode_max_depth(20) for i = 1, #arg do run_test("decode cycle " .. arg[i], test_decode_cycle, { arg[i] }, true, { true }) -- cgit v1.2.3-55-g6feb