300 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			300 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| R"-++**++-(
 | |
| -----------------------------------------------------------------------------
 | |
| -- LTN12 - Filters, sources, sinks and pumps.
 | |
| -- LuaSocket toolkit.
 | |
| -- Author: Diego Nehab
 | |
| -----------------------------------------------------------------------------
 | |
| 
 | |
| -----------------------------------------------------------------------------
 | |
| -- Declare module
 | |
| -----------------------------------------------------------------------------
 | |
| local string = require("string")
 | |
| local table = require("table")
 | |
| local base = _G
 | |
| local _M = {}
 | |
| if module then -- heuristic for exporting a global package table
 | |
|     ltn12 = _M
 | |
| end
 | |
| local filter,source,sink,pump = {},{},{},{}
 | |
| 
 | |
| _M.filter = filter
 | |
| _M.source = source
 | |
| _M.sink = sink
 | |
| _M.pump = pump
 | |
| 
 | |
| -- 2048 seems to be better in windows...
 | |
| _M.BLOCKSIZE = 2048
 | |
| _M._VERSION = "LTN12 1.0.3"
 | |
| 
 | |
| -----------------------------------------------------------------------------
 | |
| -- Filter stuff
 | |
| -----------------------------------------------------------------------------
 | |
| -- returns a high level filter that cycles a low-level filter
 | |
| function filter.cycle(low, ctx, extra)
 | |
|     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 arg = {...}
 | |
|     local n = select('#',...)
 | |
|     local top, index = 1, 1
 | |
|     local retry = ""
 | |
|     return function(chunk)
 | |
|         retry = chunk and retry
 | |
|         while true do
 | |
|             if index == top then
 | |
|                 chunk = arg[index](chunk)
 | |
|                 if chunk == "" or top == n then return chunk
 | |
|                 elseif chunk then index = index + 1
 | |
|                 else
 | |
|                     top = top+1
 | |
|                     index = top
 | |
|                 end
 | |
|             else
 | |
|                 chunk = arg[index](chunk or "")
 | |
|                 if chunk == "" then
 | |
|                     index = index - 1
 | |
|                     chunk = retry
 | |
|                 elseif chunk then
 | |
|                     if index == n then return chunk
 | |
|                     else index = index + 1 end
 | |
|                 else base.error("filter returned inappropriate nil") end
 | |
|             end
 | |
|         end
 | |
|     end
 | |
| end
 | |
| 
 | |
| -----------------------------------------------------------------------------
 | |
| -- 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(_M.BLOCKSIZE)
 | |
|             if not chunk then handle:close() end
 | |
|             return chunk
 | |
|         end
 | |
|     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)
 | |
|     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+_M.BLOCKSIZE-1)
 | |
|             i = i + _M.BLOCKSIZE
 | |
|             if chunk ~= "" then return chunk
 | |
|             else return nil end
 | |
|         end
 | |
|     else return source.empty() end
 | |
| end
 | |
| 
 | |
| -- creates rewindable source
 | |
| function source.rewind(src)
 | |
|     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
 | |
| 
 | |
| function source.chain(src, f)
 | |
|     base.assert(src and f)
 | |
|     local last_in, last_out = "", ""
 | |
|     local state = "feeding"
 | |
|     local err
 | |
|     return function()
 | |
|         if not last_out then
 | |
|             base.error('source is empty!', 2)
 | |
|         end
 | |
|         while true do
 | |
|             if state == "feeding" then
 | |
|                 last_in, err = src()
 | |
|                 if err then return nil, err end
 | |
|                 last_out = f(last_in)
 | |
|                 if not last_out then
 | |
|                     if last_in then
 | |
|                         base.error('filter returned inappropriate nil')
 | |
|                     else
 | |
|                         return nil
 | |
|                     end
 | |
|                 elseif last_out ~= "" then
 | |
|                     state = "eating"
 | |
|                     if last_in then last_in = "" end
 | |
|                     return last_out
 | |
|                 end
 | |
|             else
 | |
|                 last_out = f(last_in)
 | |
|                 if last_out == "" then
 | |
|                     if last_in == "" then
 | |
|                         state = "feeding"
 | |
|                     else
 | |
|                         base.error('filter returned ""')
 | |
|                     end
 | |
|                 elseif not last_out then
 | |
|                     if last_in then
 | |
|                         base.error('filter returned inappropriate nil')
 | |
|                     else
 | |
|                         return nil
 | |
|                     end
 | |
|                 else
 | |
|                     return last_out
 | |
|                 end
 | |
|             end
 | |
|         end
 | |
|     end
 | |
| end
 | |
| 
 | |
| -- creates a source that produces contents of several sources, one after the
 | |
| -- other, as if they were concatenated
 | |
| -- (thanks to Wim Couwenberg)
 | |
| function source.cat(...)
 | |
|     local arg = {...}
 | |
|     local src = table.remove(arg, 1)
 | |
|     return function()
 | |
|         while src do
 | |
|             local chunk, err = src()
 | |
|             if chunk then return chunk end
 | |
|             if err then return nil, err end
 | |
|             src = table.remove(arg, 1)
 | |
|         end
 | |
|     end
 | |
| end
 | |
| 
 | |
| -----------------------------------------------------------------------------
 | |
| -- 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)
 | |
|     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)
 | |
|     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
 | |
| 
 | |
| -----------------------------------------------------------------------------
 | |
| -- 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)
 | |
|     if chunk and ret then return 1
 | |
|     else return nil, src_err or snk_err end
 | |
| end
 | |
| 
 | |
| -- pumps all data from a source to a sink, using a step function
 | |
| function pump.all(src, snk, step)
 | |
|     base.assert(src and snk)
 | |
|     step = step or pump.step
 | |
|     while true do
 | |
|         local ret, err = step(src, snk)
 | |
|         if not ret then
 | |
|             if err then return nil, err
 | |
|             else return 1 end
 | |
|         end
 | |
|     end
 | |
| end
 | |
| 
 | |
| return _M
 | |
| )-++**++-"; |