You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

152 lines
3.9 KiB
Lua

--[[lit-meta
name = "creationix/coro-wrapper"
version = "3.1.0"
homepage = "https://github.com/luvit/lit/blob/master/deps/coro-wrapper.lua"
description = "An adapter for applying decoders to coro-streams."
tags = {"coro", "decoder", "adapter"}
license = "MIT"
author = { name = "Tim Caswell" }
]]
local concat = table.concat
local sub = string.sub
-- Merger allows for effecient merging of many chunks.
-- The scan function returns truthy when the chunk contains a useful delimeter
-- Or in other words, when there is enough data to flush to the decoder.
-- merger(read, scan) -> read, updateScan
-- read() -> chunk or nil
-- scan(chunk) -> should_flush
-- updateScan(scan)
local function merger(read, scan)
local parts = {}
-- Return a new read function that combines chunks smartly
return function ()
while true do
-- Read the next event from upstream.
local chunk = read()
-- We got an EOS (end of stream)
if not chunk then
-- If there is nothing left to flush, emit EOS here.
if #parts == 0 then return end
-- Flush the buffer
chunk = concat(parts)
parts = {}
return chunk
end
-- Accumulate the chunk
parts[#parts + 1] = chunk
-- Flush the buffer if scan tells us to.
if scan(chunk) then
chunk = concat(parts)
parts = {}
return chunk
end
end
end,
-- This is used to update or disable the scan function. It's useful for
-- protocols that change mid-stream (like HTTP upgrades in websockets)
function (newScan)
scan = newScan
end
end
-- Decoder takes in a read function and a decode function and returns a new
-- read function that emits decoded events. When decode returns `nil` it means
-- that it needs more data before it can parse. The index output in decode is
-- the index to start the next decode. If output index if nil it means nothing
-- is leftover and next decode starts fresh.
-- decoder(read, decode) -> read, updateDecode
-- read() -> chunk or nil
-- decode(chunk, index) -> nil or (data, index)
-- updateDecode(Decode)
local function decoder(read, decode)
local buffer, index
local want = true
return function ()
while true do
-- If there isn't enough data to decode then get more data.
if want then
local chunk = read()
if buffer then
-- If we had leftover data in the old buffer, trim it down.
if index > 1 then
buffer = sub(buffer, index)
index = 1
end
if chunk then
-- Concatenate the chunk with the old data
buffer = buffer .. chunk
end
else
-- If there was no leftover data, set new data in the buffer
if chunk then
buffer = chunk
index = 1
else
buffer = nil
index = nil
end
end
end
-- Return nil if the buffer is empty
if buffer == '' or buffer == nil then
return nil
end
-- If we have data, lets try to decode it
local item, newIndex = decode(buffer, index)
want = not newIndex
if item or newIndex then
-- There was enough data to emit an event!
if newIndex then
assert(type(newIndex) == "number", "index must be a number if set")
-- There was leftover data
index = newIndex
else
want = true
-- There was no leftover data
buffer = nil
index = nil
end
-- Emit the event
return item
end
end
end,
function (newDecode)
decode = newDecode
end
end
local function encoder(write, encode)
return function (item)
if not item then
return write()
end
return write(encode(item))
end,
function (newEncode)
encode = newEncode
end
end
return {
merger = merger,
decoder = decoder,
encoder = encoder,
}