From 16c0fd45defae13470649815d1515ed1810b5190 Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Fri, 9 Jul 2021 18:07:06 -0400 Subject: [PATCH] Reimplement queues as circular buffers --- luprex/core/cpp/luastack.cpp | 5 ++ luprex/core/cpp/luastack.hpp | 1 + luprex/core/cpp/table.cpp | 130 ++++++++++++++++++++++++----------- luprex/core/cpp/table.hpp | 18 +---- luprex/core/lua/ut-table.lua | 15 ++-- 5 files changed, 106 insertions(+), 63 deletions(-) diff --git a/luprex/core/cpp/luastack.cpp b/luprex/core/cpp/luastack.cpp index 1f19f064..9de1fb9e 100644 --- a/luprex/core/cpp/luastack.cpp +++ b/luprex/core/cpp/luastack.cpp @@ -34,6 +34,11 @@ lua_Integer LuaStack::ckinteger(LuaSlot s) const { return lua_tointeger(L_, s); } +int LuaStack::ckint(LuaSlot s) const { + luaL_checktype(L_, s, LUA_TNUMBER); + return (int)lua_tointeger(L_, s); +} + lua_Number LuaStack::cknumber(LuaSlot s) const { luaL_checktype(L_, s, LUA_TNUMBER); return lua_tonumber(L_, s); diff --git a/luprex/core/cpp/luastack.hpp b/luprex/core/cpp/luastack.hpp index 4e3912a7..f1d0530b 100644 --- a/luprex/core/cpp/luastack.hpp +++ b/luprex/core/cpp/luastack.hpp @@ -382,6 +382,7 @@ public: bool ckboolean(LuaSlot s) const; lua_Integer ckinteger(LuaSlot s) const; + int ckint(LuaSlot s) const; lua_Number cknumber(LuaSlot s) const; std::string ckstring(LuaSlot s) const; lua_State *ckthread(LuaSlot s) const; diff --git a/luprex/core/cpp/table.cpp b/luprex/core/cpp/table.cpp index 6508d045..6374b76b 100644 --- a/luprex/core/cpp/table.cpp +++ b/luprex/core/cpp/table.cpp @@ -131,76 +131,127 @@ LuaDefine(table_clear, "c") { return LS.result(); } +///////////////////////////////////////////////////////////// +// +// Queue operators. +// +///////////////////////////////////////////////////////////// + +#define QUEUE_POP 1 +#define QUEUE_FILL 2 +#define QUEUE_MAX 3 +#define QUEUE_BASE 4 + LuaDefine(queue_create, "c") { LuaRet queue; LuaStack LS(L, queue); - - LS.newtable(queue); - LS.rawset(queue, "head", 1000000); - LS.rawset(queue, "tail", 1000000); + const int imax = 8; + LS.createtable(queue, QUEUE_BASE + imax - 1, 0); + LS.rawseti(queue, QUEUE_POP, 0); + LS.rawseti(queue, QUEUE_FILL, 0); + LS.rawseti(queue, QUEUE_MAX, imax); + for (int i = 0; i < imax; i++) { + LS.rawseti(queue, QUEUE_BASE + i, 0); + } return LS.result(); } + LuaDefine(queue_push, "c") { LuaArg queue, elt; - lua_Integer head; - LuaStack LS(L, queue, elt); - - LS.rawget(head, queue, "head"); - LS.rawset(queue, head, elt); - LS.rawset(queue, "head", head+1); + LuaVar qpop, qfill, qmax, t; + LuaStack LS(L, queue, elt, qpop, qfill, qmax, t); + LS.checktable(queue); + LS.rawgeti(qpop, queue, QUEUE_POP); + LS.rawgeti(qfill, queue, QUEUE_FILL); + LS.rawgeti(qmax, queue, QUEUE_MAX); + int pop = LS.ckint(qpop); + int fill = LS.ckint(qfill); + int max = LS.ckint(qmax); + if (fill == max) { + int overflow = (pop + fill) - max; + if (overflow < 0) overflow = 0; + for (int i = 0; i < overflow; i++) { + lua_rawgeti(L, queue.index(), QUEUE_BASE + i); + lua_rawseti(L, queue.index(), QUEUE_BASE + i + max); + lua_pushinteger(L, 0); + lua_rawseti(L, queue.index(), QUEUE_BASE + i); + } + for (int i = overflow; i < max; i++) { + lua_pushinteger(L, 0); + lua_rawseti(L, queue.index(), QUEUE_BASE + i + max); + } + max *= 2; + LS.rawseti(queue, QUEUE_MAX, max); + } + int target = pop + fill; + if (target >= max) target -= max; + LS.rawseti(queue, QUEUE_BASE + target, elt); + LS.rawseti(queue, QUEUE_FILL, fill + 1); return LS.result(); } LuaDefine(queue_pop, "c") { LuaArg queue; - LuaRet elt; - lua_Integer head, tail; - LuaStack LS(L, queue, elt); - - LS.rawget(tail, queue, "tail"); - LS.rawget(head, queue, "head"); - if (head == tail) { - LS.set(elt, LuaNil); - } else { - LS.rawget(elt, queue, tail); - LS.rawset(queue, tail, LuaNil); - LS.rawset(queue, "tail", tail + 1); + LuaVar qpop, qfill, qmax, t; + LuaRet result; + LuaStack LS(L, queue, qpop, qfill, qmax, result, t); + LS.checktable(queue); + LS.rawgeti(qpop, queue, QUEUE_POP); + LS.rawgeti(qfill, queue, QUEUE_FILL); + LS.rawgeti(qmax, queue, QUEUE_MAX); + int fill = LS.ckint(qfill); + if (fill <= 0) { + LS.set(result, LuaNil); + return LS.result(); } + int pop = LS.ckint(qpop); + int max = LS.ckint(qmax); + LS.rawgeti(result, queue, QUEUE_BASE + pop); + LS.rawseti(queue, QUEUE_BASE + pop, 0); + int next = pop + 1; + if (next == max) next = 0; + LS.rawseti(queue, QUEUE_POP, next); + LS.rawseti(queue, QUEUE_FILL, fill - 1); return LS.result(); } LuaDefine(queue_size, "c") { LuaArg queue; LuaRet size; - lua_Number head, tail; LuaStack LS(L, queue, size); - - LS.rawget(head, queue, "head"); - LS.rawget(tail, queue, "tail"); - LS.set(size, head - tail); + LS.checktable(queue); + LS.rawget(size, queue, QUEUE_FILL); + LS.checknumber(size); return LS.result(); } LuaDefine(queue_nth, "c") { - LuaArg queue, n; + LuaArg queue, nn; + LuaVar qpop, qfill, qmax; LuaRet elt; - lua_Integer nth, head, tail; - LuaStack LS(L, queue, n, elt); - - nth = LS.ckinteger(n) - 1; - LS.rawget(head, queue, "head"); - LS.rawget(tail, queue, "tail"); - if ((nth < 0) || (nth + tail >= head)) { - luaL_error(L, "index out of range"); + LuaStack LS(L, queue, nn, qpop, qfill, qmax, elt); + LS.checktable(queue); + int n = LS.ckint(nn) - 1; + LS.rawgeti(qfill, queue, QUEUE_FILL); + int fill = LS.ckint(qfill); + if ((n < 0) || (n >= fill)) { + LS.set(elt, LuaNil); + return LS.result(); } - LS.rawget(elt, queue, tail + nth); + LS.rawgeti(qpop, queue, QUEUE_POP); + LS.rawgeti(qmax, queue, QUEUE_MAX); + int pop = LS.ckint(qpop); + int max = LS.ckint(qmax); + int index = pop + n; + if (index > max) index -= max; + LS.rawgeti(elt, queue, QUEUE_BASE + index); return LS.result(); } ///////////////////////////////////////////////////////////// // -// table_sortedpairs +// table_getpairs // // Given a table, return all the pairs in the table as a sequence. // The resulting sequence contains a 1 in the first position, @@ -209,6 +260,7 @@ LuaDefine(queue_nth, "c") { // sortedpairs({a=1,b=2,c=3}) => {1,"a",1,"b",2,"c",3} // // Note that this function is not directly exposed to lua. +// It's exposed only through the 'sortedpairs' iterator. // ///////////////////////////////////////////////////////////// @@ -307,7 +359,7 @@ static void auxsort (lua_State *L, int tab, int l, int u) { } /* repeat the routine for the larger one */ } -int table_getpairs(lua_State *L, bool sort, bool *unsortable) { +static int table_getpairs(lua_State *L, bool sort, bool *unsortable) { lua_checkstack(L, 40); LuaArg tab; LuaVar key, value; diff --git a/luprex/core/cpp/table.hpp b/luprex/core/cpp/table.hpp index daf12a4d..799a8f23 100644 --- a/luprex/core/cpp/table.hpp +++ b/luprex/core/cpp/table.hpp @@ -72,20 +72,6 @@ int table_empty(lua_State *L); // int table_count(lua_State *L); -// table_sortedpairs -// -// Return a vector containing pairs from the table. -// This is not exposed directly to lua, but it is used -// in the new version of the 'pairs' iterator. -// -// The boolean 'sort' is used to control whether the pairs -// are to be sorted or not. -// -// The boolean 'unsortable' returns a flag indicating -// whether or not unsortable items were omitted. -// -int table_getpairs(lua_State *L, bool sort, bool *unsortable); - // table_clear // // Remove all key/value pairs from the table. Does not @@ -95,7 +81,9 @@ int table_clear(lua_State *L); // queue_create // -// Create and return an empty queue. +// Create and return an empty queue. Queues are implemented +// as tables which are used as dynamically-expandable circular +// buffers. // int queue_create(lua_State *L); diff --git a/luprex/core/lua/ut-table.lua b/luprex/core/lua/ut-table.lua index b91d70cf..bb8b1bba 100644 --- a/luprex/core/lua/ut-table.lua +++ b/luprex/core/lua/ut-table.lua @@ -58,26 +58,23 @@ end function unittests.queues() local q = queue.create() - assert(q.head == 1000000) - assert(q.tail == 1000000) assert(queue.size(q) == 0) - assert(table.count(q) == 2) queue.push(q, 27) assert(queue.size(q) == 1) queue.push(q, 45) assert(queue.nth(q, 1) == 27) assert(queue.nth(q, 2) == 45) assert(queue.size(q) == 2) - assert(table.count(q) == 4) assert(queue.pop(q) == 27) assert(queue.size(q) == 1) - assert(table.count(q) == 3) assert(queue.pop(q) == 45) assert(queue.size(q) == 0) - assert(table.count(q) == 2) assert(queue.pop(q) == nil) - assert(table.count(q) == 2) - assert(q.head == 1000002) - assert(q.tail == 1000002) assert(queue.size(q) == 0) + for i=1,50 do + queue.push(q,i) + end + for i=1,50 do + assert(queue.pop(q)==i) + end end