#include #include #include #include #include // __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler. // We use -march=native with gmake to enable -msse2 and -msse4.2, if supported. #if defined(__SSE4_2__) # define RAPIDJSON_SSE42 #elif defined(__SSE2__) # define RAPIDJSON_SSE2 #endif #include "rapidjson/document.h" #include "rapidjson/encodedstream.h" #include "rapidjson/error/en.h" #include "rapidjson/error/error.h" #include "rapidjson/filereadstream.h" #include "rapidjson/filewritestream.h" #include "rapidjson/prettywriter.h" #include "rapidjson/rapidjson.h" #include "rapidjson/reader.h" #include "rapidjson/schema.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" #include "Userdata.hpp" #include "values.hpp" #include "luax.hpp" #include "file.hpp" #include "StringStream.hpp" using namespace rapidjson; #ifndef LUA_RAPIDJSON_VERSION #define LUA_RAPIDJSON_VERSION "scm" #endif static void createSharedMeta(lua_State* L, const char* meta, const char* type) { luaL_newmetatable(L, meta); // [meta] lua_pushstring(L, type); // [meta, name] lua_setfield(L, -2, "__jsontype"); // [meta] lua_pop(L, 1); // [] } static int makeTableType(lua_State* L, int idx, const char* meta, const char* type) { bool isnoarg = lua_isnoneornil(L, idx); bool istable = lua_istable(L, idx); if (!isnoarg && !istable) return luaL_argerror(L, idx, "optional table excepted"); if (isnoarg) lua_createtable(L, 0, 0); // [table] else // is table. { lua_pushvalue(L, idx); // [table] if (lua_getmetatable(L, -1)) { // already have a metatable, just set the __jsontype field. // [table, meta] lua_pushstring(L, type); // [table, meta, name] lua_setfield(L, -2, "__jsontype"); // [table, meta] lua_pop(L, 1); // [table] return 1; } // else fall through } // Now we have a table without meta table luaL_getmetatable(L, meta); // [table, meta] lua_setmetatable(L, -2); // [table] return 1; } static int json_object(lua_State* L) { return makeTableType(L, 1, "json.object", "object"); } static int json_array(lua_State* L) { return makeTableType(L, 1, "json.array", "array"); } static int json_decode(lua_State* L) { size_t len = 0; const char* contents = nullptr; switch(lua_type(L, 1)) { case LUA_TSTRING: contents = luaL_checklstring(L, 1, &len); break; case LUA_TLIGHTUSERDATA: contents = reinterpret_cast(lua_touserdata(L, 1)); len = luaL_checkinteger(L, 2); break; default: return luaL_argerror(L, 1, "required string or lightuserdata (points to a memory of a string)"); } rapidjson::extend::StringStream s(contents, len); return values::pushDecoded(L, s); } static int json_load(lua_State* L) { const char* filename = luaL_checklstring(L, 1, NULL); FILE* fp = file::open(filename, "rb"); if (fp == NULL) luaL_error(L, "error while open file: %s", filename); char buffer[512]; FileReadStream fs(fp, buffer, sizeof(buffer)); AutoUTFInputStream eis(fs); int n = values::pushDecoded(L, eis); fclose(fp); return n; } struct Key { Key(const char* k, SizeType l) : key(k), size(l) {} bool operator void encodeValue(lua_State* L, Writer* writer, int idx, int depth = 0) { size_t len; const char* s; int64_t integer; int t = lua_type(L, idx); switch (t) { case LUA_TBOOLEAN: writer->Bool(lua_toboolean(L, idx) != 0); return; case LUA_TNUMBER: if (luax::isinteger(L, idx, &integer)) writer->Int64(integer); else { if (!writer->Double(lua_tonumber(L, idx))) luaL_error(L, "error while encode double value."); } return; case LUA_TSTRING: s = lua_tolstring(L, idx, &len); writer->String(s, static_cast(len)); return; case LUA_TTABLE: return encodeTable(L, writer, idx, depth + 1); case LUA_TNIL: writer->Null(); return; case LUA_TLIGHTUSERDATA: if (values::isnull(L, idx)) { writer->Null(); return; } // otherwise fall thought case LUA_TFUNCTION: // fall thought case LUA_TUSERDATA: // fall thought case LUA_TTHREAD: // fall thought case LUA_TNONE: // fall thought default: luaL_error(L, "unsupported value type : %s", lua_typename(L, t)); } } template void encodeTable(lua_State* L, Writer* writer, int idx, int depth) { if (depth > max_depth) luaL_error(L, "nested too depth"); if (!lua_checkstack(L, 4)) // requires at least 4 slots in stack: table, key, value, key luaL_error(L, "stack overflow"); idx = luax::absindex(L, idx); if (values::isarray(L, idx, empty_table_as_array)) { encodeArray(L, writer, idx, depth); return; } // is object. if (!sort_keys) { encodeObject(L, writer, idx, depth); return; } std::vector keys; keys.reserve(luax::rawlen(L, idx)); lua_pushnil(L); // [nil] while (lua_next(L, idx)) { // [key, value] if (lua_type(L, -2) == LUA_TSTRING) { size_t len = 0; const char* key = lua_tolstring(L, -2, &len); keys.push_back(Key(key, static_cast(len))); } // pop value, leaving original key lua_pop(L, 1); // [key] } // [] encodeObject(L, writer, idx, depth, keys); } template void encodeObject(lua_State* L, Writer* writer, int idx, int depth) { idx = luax::absindex(L, idx); writer->StartObject(); // [] lua_pushnil(L); // [nil] while (lua_next(L, idx)) { // [key, value] if (lua_type(L, -2) == LUA_TSTRING) { size_t len = 0; const char* key = lua_tolstring(L, -2, &len); writer->Key(key, static_cast(len)); encodeValue(L, writer, -1, depth); } // pop value, leaving original key lua_pop(L, 1); // [key] } // [] writer->EndObject(); } template void encodeObject(lua_State* L, Writer* writer, int idx, int depth, std::vector &keys) { // [] idx = luax::absindex(L, idx); writer->StartObject(); std::sort(keys.begin(), keys.end()); std::vector::const_iterator i = keys.begin(); std::vector::const_iterator e = keys.end(); for (; i != e; ++i) { writer->Key(i->key, static_cast(i->size)); lua_pushlstring(L, i->key, i->size); // [key] lua_gettable(L, idx); // [value] encodeValue(L, writer, -1, depth); lua_pop(L, 1); // [] } // [] writer->EndObject(); } template void encodeArray(lua_State* L, Writer* writer, int idx, int depth) { // [] idx = luax::absindex(L, idx); writer->StartArray(); int MAX = static_cast(luax::rawlen(L, idx)); // lua_rawlen always returns value >= 0 for (int n = 1; n EndArray(); // [] } public: template void encode(lua_State* L, Stream* s, int idx) { if (pretty) { PrettyWriter writer(*s); encodeValue(L, &writer, idx); } else { Writer writer(*s); encodeValue(L, &writer, idx); } } }; static int json_encode(lua_State* L) { try{ Encoder encode(L, 2); StringBuffer s; encode.encode(L, &s, 1); lua_pushlstring(L, s.GetString(), s.GetSize()); return 1; } catch (...) { luaL_error(L, "error while encoding"); } return 0; } static int json_dump(lua_State* L) { Encoder encoder(L, 3); const char* filename = luaL_checkstring(L, 2); FILE* fp = file::open(filename, "wb"); if (fp == NULL) luaL_error(L, "error while open file: %s", filename); char buffer[512]; FileWriteStream fs(fp, buffer, sizeof(buffer)); encoder.encode(L, &fs, 1); fclose(fp); return 0; } namespace values { static intptr_t null = 0; /** * Push a value equals rapidjson.null on to the stack. */ int push_null(lua_State* L) { lua_pushlightuserdata(L, &values::null); return 1; } } static const luaL_Reg methods[] = { // string lua table { "decode", json_decode }, { "encode", json_encode }, // file lua table { "load", json_load }, { "dump", json_dump }, // special functions { "object", json_object }, { "array", json_array }, // JSON types { "Document", Userdata::create }, { "SchemaDocument", Userdata::create }, { "SchemaValidator", Userdata::create }, {NULL, NULL } }; extern "C" { LUALIB_API int luaopen_rapidjson(lua_State* L) { lua_newtable(L); // [rapidjson] luax::setfuncs(L, methods); // [rapidjson] lua_pushliteral(L, "rapidjson"); // [rapidjson, name] lua_setfield(L, -2, "_NAME"); // [rapidjson] lua_pushliteral(L, LUA_RAPIDJSON_VERSION); // [rapidjson, version] lua_setfield(L, -2, "_VERSION"); // [rapidjson] values::push_null(L); // [rapidjson, json.null] lua_setfield(L, -2, "null"); // [rapidjson] createSharedMeta(L, "json.object", "object"); createSharedMeta(L, "json.array", "array"); Userdata::luaopen(L); Userdata::luaopen(L); Userdata::luaopen(L); return 1; } }