luasocket/src/ltn12.lua

280 lines
7.6 KiB
Lua
Raw Normal View History

2004-06-04 17:15:45 +02:00
-----------------------------------------------------------------------------
-- LTN12 - Filters, sources, sinks and pumps.
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- RCS ID: $Id$
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module
-----------------------------------------------------------------------------
2004-11-27 08:58:04 +01:00
local string = require("string")
local table = require("table")
local base = require("base")
local coroutine = require("coroutine")
local ltn12 = module("ltn12")
filter = {}
source = {}
sink = {}
pump = {}
-- 2048 seems to be better in windows...
BLOCKSIZE = 2048
2004-06-04 17:15:45 +02:00
-----------------------------------------------------------------------------
-- Filter stuff
-----------------------------------------------------------------------------
-- returns a high level filter that cycles a low-level filter
function filter.cycle(low, ctx, extra)
2004-11-27 08:58:04 +01:00
base.assert(low)
return function(chunk)
local ret
ret, ctx = low(ctx, chunk, extra)
return ret
end
end
-- chains a bunch of filters together
-- (thanks to Wim Couwenberg)
function filter.chain(...)
local n = table.getn(arg)
local top, index = 1, 1
2004-11-28 03:36:07 +01:00
local retry = ""
2004-07-29 07:11:21 +02:00
return function(chunk)
2004-11-28 03:36:07 +01:00
retry = chunk and retry
while true do
if index == top then
chunk = arg[index](chunk)
2004-11-28 03:36:07 +01:00
if chunk == "" or top == n then return chunk
elseif chunk then index = index + 1
else
top = top+1
index = top
end
else
2004-11-28 03:36:07 +01:00
chunk = arg[index](chunk or "")
if chunk == "" then
index = index - 1
2004-11-28 03:36:07 +01:00
chunk = retry
elseif chunk then
if index == n then return chunk
else index = index + 1 end
2004-11-28 03:36:07 +01:00
else base.error("filter returned inappropriate nil") end
2004-11-17 05:55:57 +01:00
end
end
end
end
2004-06-04 17:15:45 +02:00
-----------------------------------------------------------------------------
-- Source stuff
-----------------------------------------------------------------------------
-- create an empty source
local function empty()
return nil
end
function source.empty()
return empty
end
-- returns a source that just outputs an error
function source.error(err)
return function()
return nil, err
end
end
-- creates a file source
function source.file(handle, io_err)
if handle then
return function()
local chunk = handle:read(BLOCKSIZE)
if not chunk then handle:close() end
return chunk
end
2004-03-19 06:04:03 +01:00
else return source.error(io_err or "unable to open file") end
end
-- turns a fancy source into a simple source
function source.simplify(src)
2004-11-27 08:58:04 +01:00
base.assert(src)
return function()
local chunk, err_or_new = src()
src = err_or_new or src
if not chunk then return nil, err_or_new
else return chunk end
end
end
-- creates string source
function source.string(s)
if s then
local i = 1
return function()
local chunk = string.sub(s, i, i+BLOCKSIZE-1)
i = i + BLOCKSIZE
if chunk ~= "" then return chunk
else return nil end
end
else return source.empty() end
end
-- creates rewindable source
function source.rewind(src)
2004-11-27 08:58:04 +01:00
base.assert(src)
local t = {}
return function(chunk)
if not chunk then
chunk = table.remove(t)
if not chunk then return src()
else return chunk end
else
table.insert(t, chunk)
end
end
end
2004-11-28 03:36:07 +01:00
local print = print
-- chains a source with a filter
function source.chain(src, f)
2004-11-27 08:58:04 +01:00
base.assert(src and f)
local last_in, last_out = "", ""
return function()
if last_out == "" then
while true do
local err
last_in, err = src()
if err then return nil, err end
last_out = f(last_in)
if last_out ~= "" then return last_out end
if not last_in then
2004-11-28 03:36:07 +01:00
base.error('filter returned inappropriate ""')
end
end
elseif last_out then
last_out = f(last_in and "")
if last_in and not last_out then
2004-11-28 03:36:07 +01:00
base.error('filter returned inappropriate nil')
end
if last_out == "" and not last_in then
base.error(base.tostring(f) .. ' returned inappropriate ""')
end
return last_out
else
base.error("source is empty", 2)
end
end
end
-- creates a source that produces contents of several sources, one after the
-- other, as if they were concatenated
function source.cat(...)
local co = coroutine.create(function()
local i = 1
while i <= table.getn(arg) do
local chunk, err = arg[i]()
if chunk then coroutine.yield(chunk)
elseif err then return nil, err
else i = i + 1 end
end
end)
return function()
local ret, a, b = coroutine.resume(co)
if ret then return a, b
else return nil, a end
end
end
2004-06-04 17:15:45 +02:00
-----------------------------------------------------------------------------
-- Sink stuff
-----------------------------------------------------------------------------
-- creates a sink that stores into a table
function sink.table(t)
t = t or {}
local f = function(chunk, err)
if chunk then table.insert(t, chunk) end
return 1
end
return f, t
end
-- turns a fancy sink into a simple sink
function sink.simplify(snk)
2004-11-27 08:58:04 +01:00
base.assert(snk)
return function(chunk, err)
local ret, err_or_new = snk(chunk, err)
if not ret then return nil, err_or_new end
snk = err_or_new or snk
return 1
end
end
-- creates a file sink
function sink.file(handle, io_err)
if handle then
return function(chunk, err)
if not chunk then
handle:close()
return 1
else return handle:write(chunk) end
end
else return sink.error(io_err or "unable to open file") end
end
-- creates a sink that discards data
local function null()
return 1
end
function sink.null()
return null
end
-- creates a sink that just returns an error
function sink.error(err)
return function()
return nil, err
end
end
-- chains a sink with a filter
function sink.chain(f, snk)
2004-11-27 08:58:04 +01:00
base.assert(f and snk)
return function(chunk, err)
if chunk ~= "" then
local filtered = f(chunk)
local done = chunk and ""
while true do
local ret, snkerr = snk(filtered, err)
if not ret then return nil, snkerr end
if filtered == done then return 1 end
filtered = f(done)
end
else return 1 end
end
end
2004-06-04 17:15:45 +02:00
-----------------------------------------------------------------------------
-- Pump stuff
-----------------------------------------------------------------------------
-- pumps one chunk from the source to the sink
function pump.step(src, snk)
local chunk, src_err = src()
local ret, snk_err = snk(chunk, src_err)
return chunk and ret and not src_err and not snk_err, src_err or snk_err
end
-- pumps all data from a source to a sink, using a step function
function pump.all(src, snk, step)
2004-11-27 08:58:04 +01:00
base.assert(src and snk)
step = step or pump.step
while true do
local ret, err = step(src, snk)
if not ret then return not err, err end
end
end
2004-11-27 08:58:04 +01:00
base.setmetatable(ltn12, nil)