From 425decdf7e9284d15aa726e3ae96b9942fb0e3ea Mon Sep 17 00:00:00 2001 From: IronClawTrem Date: Sun, 16 Feb 2020 03:40:06 +0000 Subject: create tremded branch --- src/script/rapidjson/rapidjson.cpp | 414 +++++++++++++++++++++++++++++++++++++ 1 file changed, 414 insertions(+) create mode 100644 src/script/rapidjson/rapidjson.cpp (limited to 'src/script/rapidjson/rapidjson.cpp') diff --git a/src/script/rapidjson/rapidjson.cpp b/src/script/rapidjson/rapidjson.cpp new file mode 100644 index 0000000..9e87812 --- /dev/null +++ b/src/script/rapidjson/rapidjson.cpp @@ -0,0 +1,414 @@ + +#include +#include +#include +#include + +#include "userdata.hpp" +#include "values.hpp" +#include "luax.hpp" +#include "file.hpp" + +#include "lua.hpp" +#include "rapidjson.h" + +using namespace rapidjson; + +#ifndef LUA_RAPIDJSON_VERSION +#define LUA_RAPIDJSON_VERSION "1.0.0" +#endif + +static void createSharedMeta(lua_State* L, const char* meta, const char* type) +{ + luaL_newmetatable(L, meta); + lua_pushstring(L, type); + lua_setfield(L, -2, "__jsontype"); + lua_pop(L, 1); +} + +static int makeTableType(lua_State* L, int idx, const char* meta, const char* type) +{ + auto isnoarg = lua_isnoneornil(L, idx); + auto istable = lua_istable(L, idx); + if (!isnoarg && !istable) + return luaL_argerror(L, idx, "optional table excepted"); + + if (isnoarg) + lua_createtable(L, 0, 0); + else // is table. + { + lua_pushvalue(L, idx); + if (lua_getmetatable(L, -1)) + { + // already have a metatable, just set the __jsontype field. + + lua_pushstring(L, type); + lua_setfield(L, -2, "__jsontype"); + lua_pop(L, 1); + return 1; + } + // else fall through + } + + // Now we have a table without meta table + luaL_getmetatable(L, meta); + lua_setmetatable(L, -2); + 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"); +} + +template +int decode(lua_State* L, Stream* s) +{ + auto top = lua_gettop(L); + values::ToLuaHandler handler(L); + Reader reader; + + ParseResult r = reader.Parse(*s, handler); + if (!r) + { + lua_settop(L, top); + lua_pushnil(L); + lua_pushfstring(L, "%s (%d)", GetParseError_En(r.Code()), r.Offset()); + return 2; + } + + return 1; +} + +static int json_decode(lua_State* L) +{ + size_t len = 0; + auto contents = luaL_checklstring(L, 1, &len); + StringStream s(contents); + return decode(L, &s); +} + +static int json_load(lua_State* L) +{ + auto filename = luaL_checklstring(L, 1, nullptr); + auto fp = file::open(filename, "rb"); + if (!fp) + luaL_error(L, "error while open file: %s", filename); + + char buffer[512]; + FileReadStream fs(fp, buffer, sizeof(buffer)); + AutoUTFInputStream eis(fs); + + auto n = decode(L, &eis); + + fclose(fp); + return n; +} + +struct Key +{ + Key(const char* k, SizeType l) : key(k), size(l) + {} + bool operator<(const Key& rhs) const + { + return strcmp(key, rhs.key) < 0; + } + const char* key; + SizeType size; +}; + +class Encoder +{ + bool pretty; + bool sort_keys; + int max_depth; + static const int MAX_DEPTH_DEFAULT = 128; + public: + Encoder(lua_State*L, int opt) : pretty(false), sort_keys(false), max_depth(MAX_DEPTH_DEFAULT) + { + if (lua_isnoneornil(L, opt)) + return; + luaL_checktype(L, opt, LUA_TTABLE); + + pretty = luax::optboolfield(L, opt, "pretty", false); + sort_keys = luax::optboolfield(L, opt, "sort_keys", false); + max_depth = luax::optintfield(L, opt, "max_depth", MAX_DEPTH_DEFAULT); + } + + private: + template + void encodeValue(lua_State* L, Writer* writer, int idx, int depth = 0) + { + size_t len; + const char* s; + int64_t integer; + auto 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_TFUNCTION: + if (values::isnull(L, idx)) + { + writer->Null(); + return; + } + // otherwise fall thought + case LUA_TLIGHTUSERDATA: // fall thought + case LUA_TUSERDATA: // fall thought + case LUA_TTHREAD: // fall thought + case LUA_TNONE: // fall thought + default: + luaL_error(L, "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"); + + lua_pushvalue(L, idx); + if (values::isarray(L, -1)) + { + encodeArray(L, writer, depth); + lua_pop(L, 1); + return; + } + + // is object. + if (!sort_keys) + { + encodeObject(L, writer, depth); + lua_pop(L, 1); + return; + } + + lua_pushnil(L); + std::vector keys; + + while (lua_next(L, -2)) + { + + + if (lua_type(L, -2) == LUA_TSTRING) + { + size_t len = 0; + auto key = lua_tolstring(L, -2, &len); + keys.push_back(Key(key, static_cast(len))); + } + + // pop value, leaving original key + lua_pop(L, 1); + + } + + encodeObject(L, writer, depth, keys); + lua_pop(L, 1); + } + + template + void encodeObject(lua_State* L, Writer* writer, int depth) + { + writer->StartObject(); + + + lua_pushnil(L); + while (lua_next(L, -2)) + { + + if (lua_type(L, -2) == LUA_TSTRING) + { + size_t len = 0; + auto 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); + + } + + writer->EndObject(); + } + + template + void encodeObject(lua_State* L, Writer* writer, int depth, std::vector &keys) + { + + writer->StartObject(); + std::sort(keys.begin(), keys.end()); + + std::vector::const_iterator i = keys.begin(); + std::vector::const_iterator e = keys.end(); + for ( auto const& i : keys ) + { + writer->Key(i.key, static_cast(i.size)); + lua_pushlstring(L, i.key, i.size); + lua_gettable(L, -2); + encodeValue(L, writer, -1, depth); + lua_pop(L, 1); + } + + writer->EndObject(); + } + + template + void encodeArray(lua_State* L, Writer* writer, int depth) + { + + writer->StartArray(); + auto MAX = static_cast(luax::rawlen(L, -1)); // lua_rawlen always returns value >= 0 + for (auto n = 1; n <= MAX; ++n) + { + lua_rawgeti(L, -1, n); + encodeValue(L, writer, -1, depth); + lua_pop(L, 1); + } + writer->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); + + auto filename = luaL_checkstring(L, 2); + auto fp = file::open(filename, "wb"); + if (fp == nullptr) + 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 auto nullref = LUA_NOREF; + /** + * Returns rapidjson.null. + */ + int json_null(lua_State* L) + { + lua_rawgeti(L, LUA_REGISTRYINDEX, nullref); + 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 tags and functions + { "null", values::json_null }, + { "object", json_object }, + { "array", json_array }, + + // JSON types + { "Document", Userdata::create }, + { "SchemaDocument", Userdata::create }, + { "SchemaValidator", Userdata::create }, + + {nullptr, nullptr } +}; + +extern "C" +{ + LUALIB_API int luaopen_rapidjson(lua_State* L) + { + lua_newtable(L); + + luax::setfuncs(L, methods); + + lua_pushliteral(L, "rapidjson"); + lua_setfield(L, -2, "_NAME"); + + lua_pushliteral(L, LUA_RAPIDJSON_VERSION); + lua_setfield(L, -2, "_VERSION"); + + lua_getfield(L, -1, "null"); + values::nullref = luaL_ref(L, LUA_REGISTRYINDEX); + + + createSharedMeta(L, "json.object", "object"); + createSharedMeta(L, "json.array", "array"); + + Userdata::luaopen(L); + Userdata::luaopen(L); + Userdata::luaopen(L); + + return 1; + } +} -- cgit