From 36e9090cba261affe49f4ba5c62676be8853996a Mon Sep 17 00:00:00 2001 From: Egor Date: Wed, 4 Feb 2026 15:07:26 +0200 Subject: [PATCH] admin: rework lua bindings to map --- src/admin.cpp | 305 +++++++++++++++++++++++++++----------------------- src/job.hpp | 5 - src/map.hpp | 77 +------------ src/util.hpp | 6 + 4 files changed, 175 insertions(+), 218 deletions(-) diff --git a/src/admin.cpp b/src/admin.cpp index e78cad6..b415b9f 100644 --- a/src/admin.cpp +++ b/src/admin.cpp @@ -8,121 +8,153 @@ #include static lua_State *GL = 0; -#define MAPTYPE_STRINGSET 0 -#define MAPTYPE_CLIENT 1 -#define MAPTYPE_CHANNEL 2 -static int map_index(lua_State *L) -{ - map_base *m = *(map_base **)luaL_checkudata(L, 1, "map"); - const char *key = luaL_checkstring(L, 2); - void *value = m->_get(key); - if (!value) - return 0; - lua_getiuservalue(L, 1, 1); - switch (lua_tointeger(L, -1)) { - case MAPTYPE_STRINGSET: - lua_pushvalue(L, 2); - return 1; - case MAPTYPE_CLIENT: - *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = (Client *)value; - luaL_setmetatable(L, "client"); - return 1; - case MAPTYPE_CHANNEL: - *(Channel **)lua_newuserdatauv(L, sizeof(Channel *), 0) = (Channel *)value; - luaL_setmetatable(L, "channel"); - return 1; - } - return 0; +static std::string_view checkstringview(lua_State *L, int idx) { + size_t size = 0; + const char *key = luaL_checklstring(L, idx, &size); + return std::string_view(key, size); } -static int map_newindex(lua_State *L) -{ - map_base *m = *(map_base **)luaL_checkudata(L, 1, "map"); - const char *key = luaL_checkstring(L, 2); - luaL_checkany(L, 3); - if (lua_isnil(L, 3)) { - m->_remove(key); - return 0; +template +struct MptrOwnerType {}; +template +struct MptrOwnerType { + using type = T; +}; + +template // +int LuaForwarder(lua_State *L) { + using T = MptrOwnerType::type; + return (static_cast(luaL_checkudata(L, 1, T::mtname()))->*P)(L); +} + +template +struct SetRef { + map_base2 &s; + + static consteval const char *mtname() { + if constexpr (std::same_as) { + return "Set"; + } else if constexpr (std::same_as) { + return "Set"; + } else if constexpr (std::same_as) { + return "Set"; + } else { + static_assert(false); + } + }; + static void pushval(lua_State *L, const T& v) requires std::same_as { + lua_pushlstring(L, v.data(), v.size()); } - lua_getiuservalue(L, 1, 1); - switch (lua_tointeger(L, -1)) { - case MAPTYPE_STRINGSET: - m->_set(key); - break; - case MAPTYPE_CLIENT: { - void **p = (void **)luaL_checkudata(L, 3, "client"); - m->_set(*p); - break; + static void pushval(lua_State *L, const T& v) requires std::same_as { + *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = v; + luaL_setmetatable(L, "Client"); } - case MAPTYPE_CHANNEL: { - void **p = (void **)luaL_checkudata(L, 3, "channel"); - m->_set(*p); - break; + static void pushval(lua_State *L, const T& v) requires std::same_as { + *(Channel **)lua_newuserdatauv(L, sizeof(Channel *), 0) = v; + luaL_setmetatable(L, "Channel"); } + + int index(lua_State *L) { + std::string_view key = checkstringview(L, 2); + if (!s.has(key)) + return 0; + pushval(L, s.get(key)); + return 1; } - return 0; -} -static int map_iter_next(lua_State *L) -{ - map_base::iter *it = (map_base::iter *)luaL_checkudata(L, 1, "map_iter"); - auto [key, value] = it->next(); - if (!value) + int newindex(lua_State *L) { + std::string_view key = checkstringview(L, 2); + luaL_checkany(L, 3); + if (lua_isnil(L, 3)) { + s.remove(key); + return 0; + } + if constexpr (std::same_as) { + s.add(std::string(key)); // FIXME: + } else if constexpr (std::same_as) { + s.add(*(Client **)luaL_checkudata(L, 3, "Client")); + } else if constexpr (std::same_as) { + s.add(*(Channel **)luaL_checkudata(L, 3, "Channel")); + } else { + static_assert(false); + } return 0; - lua_getiuservalue(L, 1, 1); - switch (lua_tointeger(L, -1)) { - case MAPTYPE_STRINGSET: - lua_pushlstring(L, key.data(), key.size()); - lua_pushlstring(L, key.data(), key.size()); - return 2; - case MAPTYPE_CLIENT: - lua_pushlstring(L, key.data(), key.size()); - *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = (Client *)value; - luaL_setmetatable(L, "client"); - return 2; - case MAPTYPE_CHANNEL: - lua_pushlstring(L, key.data(), key.size()); - *(Channel **)lua_newuserdatauv(L, sizeof(Channel *), 0) = (Channel *)value; - luaL_setmetatable(L, "channel"); - return 2; } - return 0; -} -static int map_pairs(lua_State *L) -{ - map_base *m = *(map_base **)luaL_checkudata(L, 1, "map"); - lua_pushcfunction(L, map_iter_next); - map_base::iter *it = (map_base::iter *)lua_newuserdatauv(L, sizeof(map_base::iter), 1); - memset(it, 0, sizeof(*it)); - it->root = m; - luaL_setmetatable(L, "map_iter"); - lua_getiuservalue(L, 1, 1); - lua_setiuservalue(L, -2, 1); - lua_pushnil(L); - return 3; -} -static void map_push(lua_State *L, map_base *map, int type) -{ - *(map_base **)lua_newuserdatauv(L, sizeof(map_base *), 1) = map; - luaL_setmetatable(L, "map"); - lua_pushinteger(L, type); - lua_setiuservalue(L, -2, 1); + struct Iter { + static consteval const char *mtname() { + if constexpr (std::same_as) { + return "Set::Iter"; + } else if constexpr (std::same_as) { + return "Set::Iter"; + } else if constexpr (std::same_as) { + return "Set::Iter"; + } else { + static_assert(false); + } + }; + size_t index = 0; + int next(lua_State *L) { + lua_getiuservalue(L, 1, 1); + SetRef &ref = *(SetRef *)lua_touserdata(L, -1); + auto &m = ref.s.m; + auto it = m.begin() + index; + if (it == m.end()) + return 0; + index++; + auto key = to_key(*it); + lua_pushlstring(L, key.data(), key.size()); + pushval(L, *it); + return 2; + } + }; + int pairs(lua_State *L) { + lua_pushcfunction(L, LuaForwarder<&Iter::next>); + new (lua_newuserdatauv(L, sizeof(Iter), 1)) Iter(); + luaL_setmetatable(L, Iter::mtname()); + lua_pushvalue(L, 1); + lua_setiuservalue(L, -2, 1); + lua_pushnil(L); + return 3; + } + static void push(lua_State *L, map_base2 *s) { + new (lua_newuserdatauv(L, sizeof(SetRef), 0)) SetRef(*s); + luaL_setmetatable(L, mtname()); + } + static void regmt(lua_State *L) { + if (luaL_newmetatable(L, mtname())) { + luaL_Reg funcs[] = { + {"__index", LuaForwarder<&SetRef::index>}, + {"__newindex", LuaForwarder<&SetRef::newindex>}, + {"__pairs", LuaForwarder<&SetRef::pairs>}, + {0, 0}, + }; + luaL_setfuncs(L, funcs, 0); + } + lua_pop(L, 1); + luaL_newmetatable(L, Iter::mtname()); + lua_pop(L, 1); + } +}; +template +static void SetRef_push(lua_State *L, map_base2 *s) { + SetRef::push(L, s); } + static int client_index(lua_State *L) { - Client *cl = *(Client **)luaL_checkudata(L, 1, "client"); + Client *cl = *(Client **)luaL_checkudata(L, 1, "Client"); const char *key = luaL_checkstring(L, 2); if (!cl) return 0; #define string_getter(c, s) if (!strcmp(key, #s)) return lua_pushstring(L, c->s), 1 #define integer_getter(c, s) if (!strcmp(key, #s)) return lua_pushinteger(L, c->s), 1 -#define map_getter(c, s, t) if (!strcmp(key, #s)) return map_push(L, &c->s, t), 1 +#define SetRef_getter(c, s) if (!strcmp(key, #s)) return SetRef_push(L, &c->s), 1 integer_getter(cl, fd); /* cctx */ if (!strcmp(key, "next")) { if (!cl->next) return 0; *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = cl->next; - luaL_setmetatable(L, "client"); + luaL_setmetatable(L, "Client"); return 1; } string_getter(cl, nick); @@ -144,9 +176,9 @@ static int client_index(lua_State *L) /* rbuf/rlen/wbuf/wlen */ integer_getter(cl, umode); integer_getter(cl, regstate); - map_getter(cl, channels, MAPTYPE_CHANNEL); - map_getter(cl, invites, MAPTYPE_CHANNEL); - map_getter(cl, monitor, MAPTYPE_STRINGSET); + SetRef_getter(cl, channels); + SetRef_getter(cl, invites); + SetRef_getter(cl, monitor); integer_getter(cl, cookie); integer_getter(cl, signon); integer_getter(cl, lastactivity); @@ -166,7 +198,7 @@ static int client_index(lua_State *L) } static int channel_index(lua_State *L) { - Channel *chan = *(Channel **)luaL_checkudata(L, 1, "channel"); + Channel *chan = *(Channel **)luaL_checkudata(L, 1, "Channel"); const char *key = luaL_checkstring(L, 2); if (!chan) return 0; @@ -176,18 +208,18 @@ static int channel_index(lua_State *L) string_getter(chan, topic_who); integer_getter(chan, topic_when); integer_getter(chan, creation); - map_getter(chan, members, MAPTYPE_CLIENT); - map_getter(chan, o, MAPTYPE_CLIENT); - map_getter(chan, v, MAPTYPE_CLIENT); + SetRef_getter(chan, members); + SetRef_getter(chan, o); + SetRef_getter(chan, v); integer_getter(chan, member_count); integer_getter(chan, dmode); - map_getter(chan, listb, MAPTYPE_STRINGSET); - map_getter(chan, liste, MAPTYPE_STRINGSET); - map_getter(chan, listI, MAPTYPE_STRINGSET); - map_getter(chan, listq, MAPTYPE_STRINGSET); - map_getter(chan, listwo, MAPTYPE_STRINGSET); - map_getter(chan, listwv, MAPTYPE_STRINGSET); - map_getter(chan, invitees, MAPTYPE_CLIENT); + SetRef_getter(chan, listb); + SetRef_getter(chan, liste); + SetRef_getter(chan, listI); + SetRef_getter(chan, listq); + SetRef_getter(chan, listwo); + SetRef_getter(chan, listwv); + SetRef_getter(chan, invitees); string_getter(chan, chankey); integer_getter(chan, member_limit); return 0; @@ -196,17 +228,17 @@ static int globals_index(lua_State *L) { const char *key = luaL_checkstring(L, 2); if (!strcmp(key, "client_map")) - return map_push(L, &client_map, MAPTYPE_CLIENT), 1; + return SetRef_push(L, &client_map), 1; if (!strcmp(key, "channel_map")) - return map_push(L, &channel_map, MAPTYPE_CHANNEL), 1; + return SetRef_push(L, &channel_map), 1; if (!strcmp(key, "clients")) { *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = clients; - luaL_setmetatable(L, "client"); + luaL_setmetatable(L, "Client"); return 1; } if (!strcmp(key, "dyingclients")) { *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = dyingclients; - luaL_setmetatable(L, "client"); + luaL_setmetatable(L, "Client"); return 1; } return 0; @@ -222,25 +254,25 @@ static int command_eval(lua_State *L) } static int fn_client_privmsg(lua_State *L) { - Client *cl = *(Client **)luaL_checkudata(L, 1, "client"); + Client *cl = *(Client **)luaL_checkudata(L, 1, "Client"); const char *msg = luaL_checkstring(L, 2); msg_privmsg_server(luaL_optstring(L, 3, hostname), cl->nick, msg), sendbuf(cl); return 0; } static int fn_client_notice(lua_State *L) { - Client *cl = *(Client **)luaL_checkudata(L, 1, "client"); + Client *cl = *(Client **)luaL_checkudata(L, 1, "Client"); const char *msg = luaL_checkstring(L, 2); msg_notice_server(luaL_optstring(L, 3, hostname), cl->nick, msg), sendbuf(cl); return 0; } static int fn_channel_join(lua_State *L) { - Client *cl = *(Client **)luaL_checkudata(L, 1, "client"); + Client *cl = *(Client **)luaL_checkudata(L, 1, "Client"); const char *channel = 0; Channel *chan = 0; if (lua_isuserdata(L, 2)) { - chan = *(Channel **)luaL_checkudata(L, 2, "channel"); + chan = *(Channel **)luaL_checkudata(L, 2, "Channel"); channel = chan->key; } else { channel = luaL_checkstring(L, 2); @@ -251,12 +283,12 @@ static int fn_channel_join(lua_State *L) } static int fn_channel_part(lua_State *L) { - Client *cl = *(Client **)luaL_checkudata(L, 1, "client"); - Channel *chan = *(Channel **)luaL_checkudata(L, 2, "channel"); + Client *cl = *(Client **)luaL_checkudata(L, 1, "Client"); + Channel *chan = *(Channel **)luaL_checkudata(L, 2, "Channel"); const char *reason = luaL_optstring(L, 3, 0); Client *kicker = 0; if (lua_isuserdata(L, 4)) - kicker = *(Client **)luaL_checkudata(L, 4, "client"); + kicker = *(Client **)luaL_checkudata(L, 4, "Client"); channel_part(cl, chan, reason, kicker); return 0; } @@ -277,7 +309,7 @@ static int fn_channel_new(lua_State *L) } static int fn_channel_mode(lua_State *L) { - Channel *chan = *(Channel **)luaL_checkudata(L, 1, "channel"); + Channel *chan = *(Channel **)luaL_checkudata(L, 1, "Channel"); size_t as, bs; const char *a = luaL_checklstring(L, 2, &as); const char *b = luaL_optlstring(L, 3, 0, &bs); @@ -292,7 +324,7 @@ static int fn_channel_mode(lua_State *L) } static int fn_channel_topic(lua_State *L) { - Channel *chan = *(Channel **)luaL_checkudata(L, 1, "channel"); + Channel *chan = *(Channel **)luaL_checkudata(L, 1, "Channel"); const char *topic = luaL_checkstring(L, 2); const char *who = luaL_checkstring(L, 3); time_t when = luaL_optinteger(L, 4, 0); @@ -301,15 +333,15 @@ static int fn_channel_topic(lua_State *L) } static int fn_client_chghost(lua_State *L) { - Client *cl = *(Client **)luaL_checkudata(L, 1, "client"); + Client *cl = *(Client **)luaL_checkudata(L, 1, "Client"); const char *newusername = luaL_optstring(L, 2, 0); const char *newhostname = luaL_optstring(L, 3, 0); client_chghost(cl, newusername, newhostname); return 0; } static int fn_rpl_whoisspecial(lua_State *L) { - Client *cl1 = *(Client **)luaL_checkudata(L, 1, "client"); - Client *cl2 = *(Client **)luaL_checkudata(L, 2, "client"); + Client *cl1 = *(Client **)luaL_checkudata(L, 1, "Client"); + Client *cl2 = *(Client **)luaL_checkudata(L, 2, "Client"); const char *text = luaL_checkstring(L, 3); rpl_whoisspecial(cl1, cl2, "", text), sendbuf(cl1); return 0; @@ -373,19 +405,10 @@ void admin_init(void) lua_State *L = luaL_newstate(); GL = L; luaL_openlibs(L); - if (luaL_newmetatable(L, "map")) { - luaL_Reg funcs[] = { - {"__index", map_index}, - {"__newindex", map_newindex}, - {"__pairs", map_pairs}, - {0, 0}, - }; - luaL_setfuncs(L, funcs, 0); - } - lua_pop(L, 1); - luaL_newmetatable(L, "map_iter"); - lua_pop(L, 1); - if (luaL_newmetatable(L, "client")) { + SetRef::regmt(L); + SetRef::regmt(L); + SetRef::regmt(L); + if (luaL_newmetatable(L, "Client")) { luaL_Reg funcs[] = { {"__index", client_index}, {0, 0}, @@ -393,7 +416,7 @@ void admin_init(void) luaL_setfuncs(L, funcs, 0); } lua_pop(L, 1); - if (luaL_newmetatable(L, "channel")) { + if (luaL_newmetatable(L, "Channel")) { luaL_Reg funcs[] = { {"__index", channel_index}, {0, 0}, @@ -475,7 +498,7 @@ bool admin_command(Client *client, char *source, char *command, int argc, char * return false; } *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = client; - luaL_setmetatable(L, "client"); + luaL_setmetatable(L, "Client"); lua_pushstring(L, source); lua_pushstring(L, command); lua_createtable(L, argc, 1); @@ -507,19 +530,19 @@ int admin_hook(const char *hook, Client *cl1, Client *cl2, Channel *chan, char * lua_pushstring(L, hook); if (cl1) { *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = cl1; - luaL_setmetatable(L, "client"); + luaL_setmetatable(L, "Client"); } else { lua_pushnil(L); } if (cl2) { *(Client **)lua_newuserdatauv(L, sizeof(Client *), 0) = cl2; - luaL_setmetatable(L, "client"); + luaL_setmetatable(L, "Client"); } else { lua_pushnil(L); } if (chan) { *(Channel **)lua_newuserdatauv(L, sizeof(Client *), 0) = chan; - luaL_setmetatable(L, "channel"); + luaL_setmetatable(L, "Channel"); } else { lua_pushnil(L); } diff --git a/src/job.hpp b/src/job.hpp index a0ca166..2f2ecd3 100644 --- a/src/job.hpp +++ b/src/job.hpp @@ -10,11 +10,6 @@ void job_fini(void); void job_process(void); void job_add(Client *client, jobproc_t&& job, contproc_t&& then); void job_kill(Client *client); -template -struct Literal { - char value[N]; - consteval Literal(const char (&s)[N]) { std::copy_n(s, N, value); } -}; template void job_password_check(Client *client, const char *key, const char *pw, contproc_t&& then); extern template void job_password_check<"ops">(Client *client, const char *key, const char *pw, contproc_t&& then); diff --git a/src/map.hpp b/src/map.hpp index 4625b8e..e437456 100644 --- a/src/map.hpp +++ b/src/map.hpp @@ -190,88 +190,21 @@ struct MapValueLess { return lhs < to_key(rhs); } }; -struct map_base { - map_base& operator=(map_base&&) = delete; - virtual ~map_base() = default; - virtual void *_get(const char *key) =0; - virtual void _set(const void *value) =0; - virtual void _remove(const char *key) =0; - virtual std::pair _next(size_t& index) =0; - struct iter { - map_base *root; - size_t index; - auto next() { - return root->_next(index); - } - }; -}; template -struct map_base2 : map_base { +struct map_base2 { + map_base2& operator=(map_base2&&) = delete; FlatSet> m; - ~map_base2() { - clear(); - } -private: - static constexpr T value_to_void(T v) noexcept requires (std::same_as || std::same_as) { - return v; - } - static constexpr char *value_to_void(const T& v) noexcept requires std::same_as { - return const_cast(v.data()); - } - using value_to_void_type = decltype(value_to_void(std::declval())); - - static constexpr T void_to_value(void *p) noexcept requires (std::same_as || std::same_as) { - return static_cast(p); - } - static constexpr std::string_view void_to_value(void *p) noexcept requires std::same_as { - return static_cast(p); - } -public: - void *_get(const char *key) final { - auto it = m.find(key); - return it != m.end() ? value_to_void(*it) : nullptr; - } - void _set(const void *value) final { - if (value) { - auto mv = void_to_value(const_cast(value)); - m.insert(mv); - } - } - void _remove(const char *key) final { - remove(key); - } - std::pair _next(size_t& index) final { - auto it = m.begin() + index; - if (it < m.end()) { - index++; - return std::make_pair(to_key(*it), static_cast(value_to_void(*it))); - } - return std::make_pair(std::string_view(), nullptr); - } void clear() { m.clear(); } using value_type = T; - struct iterator { - using iterator_category = std::bidirectional_iterator_tag; - using value_type = T; - using difference_type = ptrdiff_t; - using pointer = const T*; - using reference = const T&; - iterator &operator++() { ++ptr; return *this; } - iterator operator++(int) { auto it = *this; ++ptr; return it; } - iterator &operator--() { --ptr; return *this; } - iterator operator--(int) { auto it = *this; --ptr; return it; } - bool operator==(const iterator& other) const = default; - reference operator*() const { return *ptr; } - decltype(m)::iterator ptr; - }; + using iterator = decltype(m)::iterator; static_assert(std::bidirectional_iterator); - auto begin() { return iterator{m.begin()}; } - auto end() { return iterator{m.end()}; } + auto begin() { return m.begin(); } + auto end() { return m.end(); } struct reverse { map_base2 &obj; auto begin() { return std::make_reverse_iterator(obj.end()); } diff --git a/src/util.hpp b/src/util.hpp index 668c868..d0fee34 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -30,4 +30,10 @@ extern const char *datadir; const char *getdatafile(const char *fn); extern const char *luadir; const char *getluafile(const char *fn); + +template +struct Literal { + char value[N]; + consteval Literal(const char (&s)[N]) { std::copy_n(s, N, value); } +}; #endif -- 2.56.0