--- Remove an element and pass it to k; if that returns a function, leave that
+-- Remove an element and pass it to k; if that returns a value, leave that
-- pending at the top of the fifo. Thus, we can get events that do multiple
--- things. If the queue is empty, do not invoke k but flag it to enable
--- immediate execution at the next call to queue.
+-- things.
--
--- Returns 'true' if the queue was not empty, 'false' otherwise.
+-- If k returns nil, the fifo will be advanced. Moreover, k may return a
+-- second result, a boolean, which indicates whether or not this dequeue
+-- "counts" as one; this is useful for "phantom" elements in the fifo, such as
+-- (placeholders for) callbacks to observers, that cannot otherwise act as
+-- ordinary fifo elements do.
+--
+-- If the queue is empty, do not invoke k but flag it to enable immediate
+-- execution at the next call to queue.
+--
+-- Returns 'true' if the queue contained at least one non-phantom entry,
+-- 'false' otherwise.
local function dequeue(q,k)
if #q > 0
then
- local new = k(q[1])
+ local new, again = k(q[1])
if new == nil
then table.remove(q,1)
+ if again then return dequeue(q, k) end -- note tail call
else q[1] = new
end
return true
-- subsequent dequeues.
local function queue(q,a,k)
table.insert(q,a)
- if k ~= nil and q._go then q._go = false; q:dequeue(k) end
+ if k ~= nil and q._go then q._go = false; dequeue(q, k) end
end
-- return a FIFO constructor
return function()
local fifo = OVL.fifo()
return function(sock)
- local ssend = function(s) sock:send(s) end
+ local ssend = function(s)
+ ns = nil
+ if type(s) == "function" then s, ns = s() end
+ if s ~= nil then
+ sock:send(s)
+ return ns or nil, false
+ else
+ return nil, true
+ end
+ end
local fsmall, lsmall, fbig = {}, 0, fifo()
-- Move fsmall to fbig; might send if fbig empty
-- don't sweat the petty things
if s == nil or s == "" then return end
+ -- Our fifos can take functions; these can be useful for either lazy
+ -- generators or callbacks for parts of the stream having been sent.
+ -- Go ahead and queue this thing in the right place.
+ if type(s) == "function" then promote(); fbig:queue(s, ssend); return; end
+
-- small fifo would overfill? promote it
if lsmall + #s > BIGTHRESH or #fsmall >= FSMALLLIM then promote() end
OVL.fifo = function() return dofile("./fifo/fifo.lua") end
package.loaded["fifosock"] = dofile("./net/fifosock.lua")
--- vprint = print
-vprint = function() end
+verbose = false
+
+vprint = verbose and print or function() end
outs = {}
fakesock = {
sent() ; fcheck(string.rep("c",260))
sent() ; fchecke()
+-- test a lazy generator
+local ix = 0
+local function gen() vprint("GEN", ix); ix = ix + 1; return ("a" .. ix), ix < 3 and gen end
+fsend(gen)
+fsend("b")
+fcheck("a1")
+sent() ; fcheck("a2")
+sent() ; fcheck("a3")
+sent() ; fcheck("b")
+sent() ; fchecke()
+
+-- test a completeion-like callback that does send text
+local ix = 0
+local function gen() vprint("GEN"); ix = 1; return "efgh", nil end
+fsend("abcd"); fsend(gen); fsend("ijkl")
+assert (ix == 0)
+ fcheck("abcd"); assert (ix == 0)
+sent() ; fcheck("efgh"); assert (ix == 1); ix = 0
+sent() ; fcheck("ijkl"); assert (ix == 0)
+sent() ; fchecke()
+
+-- and one that doesn't
+local ix = 0
+local function gen() vprint("GEN"); ix = 1; return nil, nil end
+fsend("abcd"); fsend(gen); fsend("ijkl")
+assert (ix == 0)
+ fcheck("abcd"); assert (ix == 0)
+sent() ; fcheck("ijkl"); assert (ix == 1); ix = 0
+sent() ; fchecke() ; assert (ix == 0)
+
print("All tests OK")