#include <lua.hpp>
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<typename T>
+struct MptrOwnerType {};
+template<typename T, typename U>
+struct MptrOwnerType<U T::*> {
+ using type = T;
+};
+
+template<auto P> // <int(T::* P)(lua_State *L)>
+int LuaForwarder(lua_State *L) {
+ using T = MptrOwnerType<decltype(P)>::type;
+ return (static_cast<T*>(luaL_checkudata(L, 1, T::mtname()))->*P)(L);
+}
+
+template<typename T>
+struct SetRef {
+ map_base2<T> &s;
+
+ static consteval const char *mtname() {
+ if constexpr (std::same_as<T, std::string>) {
+ return "Set<std::string>";
+ } else if constexpr (std::same_as<T, Client*>) {
+ return "Set<Client*>";
+ } else if constexpr (std::same_as<T, Channel*>) {
+ return "Set<Channel*>";
+ } else {
+ static_assert(false);
+ }
+ };
+ static void pushval(lua_State *L, const T& v) requires std::same_as<T, std::string> {
+ 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<T, Client*> {
+ *(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<T, Channel*> {
+ *(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<T, std::string>) {
+ s.add(std::string(key)); // FIXME:
+ } else if constexpr (std::same_as<T, Client*>) {
+ s.add(*(Client **)luaL_checkudata(L, 3, "Client"));
+ } else if constexpr (std::same_as<T, Channel*>) {
+ 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<T, std::string>) {
+ return "Set<std::string>::Iter";
+ } else if constexpr (std::same_as<T, Client*>) {
+ return "Set<Client*>::Iter";
+ } else if constexpr (std::same_as<T, Channel*>) {
+ return "Set<Channel*>::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<T> *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<typename T>
+static void SetRef_push(lua_State *L, map_base2<T> *s) {
+ SetRef<T>::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);
/* 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);
}
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;
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;
{
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;
}
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);
}
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;
}
}
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);
}
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);
}
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;
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<std::string>::regmt(L);
+ SetRef<Client *>::regmt(L);
+ SetRef<Channel *>::regmt(L);
+ if (luaL_newmetatable(L, "Client")) {
luaL_Reg funcs[] = {
{"__index", client_index},
{0, 0},
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},
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);
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);
}