1 -----------------------------------------------------------------------------
2 -- LTN12 - Filters, sources, sinks and pumps.
5 -----------------------------------------------------------------------------
7 -----------------------------------------------------------------------------
9 -----------------------------------------------------------------------------
10 local string = require("string")
11 local table = require("table")
20 -- 2048 seems to be better in windows...
22 _VERSION
= "LTN12 1.0.2"
24 -----------------------------------------------------------------------------
26 -----------------------------------------------------------------------------
27 -- returns a high level filter that cycles a low-level filter
28 function filter
.cycle(low
, ctx
, extra
)
30 return function(chunk
)
32 ret
, ctx
= low(ctx
, chunk
, extra
)
37 -- chains a bunch of filters together
38 -- (thanks to Wim Couwenberg)
39 function filter
.chain(...)
42 local top
, index
= 1, 1
44 return function(chunk
)
45 retry
= chunk
and retry
48 chunk
= arg
[index
](chunk
)
49 if chunk
== "" or top
== n
then return chunk
50 elseif chunk
then index
= index
+ 1
56 chunk
= arg
[index
](chunk
or "")
61 if index
== n
then return chunk
62 else index
= index
+ 1 end
63 else base
.error("filter returned inappropriate nil") end
69 -----------------------------------------------------------------------------
71 -----------------------------------------------------------------------------
72 -- create an empty source
73 local function empty()
77 function source
.empty()
81 -- returns a source that just outputs an error
82 function source
.error(err
)
88 -- creates a file source
89 function source
.file(handle
, io_err
)
92 local chunk
= handle
:read(BLOCKSIZE
)
93 if not chunk
then handle
:close() end
96 else return source
.error(io_err
or "unable to open file") end
99 -- turns a fancy source into a simple source
100 function source
.simplify(src
)
103 local chunk
, err_or_new
= src()
104 src
= err_or_new
or src
105 if not chunk
then return nil, err_or_new
106 else return chunk
end
110 -- creates string source
111 function source
.string(s
)
115 local chunk
= string.sub(s
, i
, i
+BLOCKSIZE
-1)
117 if chunk
~= "" then return chunk
120 else return source
.empty() end
123 -- creates rewindable source
124 function source
.rewind(src
)
127 return function(chunk
)
129 chunk
= table.remove(t
)
130 if not chunk
then return src()
131 else return chunk
end
133 table.insert(t
, chunk
)
138 function source
.chain(src
, f
)
139 base
.assert(src
and f
)
140 local last_in
, last_out
= "", ""
141 local state
= "feeding"
145 base
.error('source is empty!', 2)
148 if state
== "feeding" then
150 if err
then return nil, err
end
151 last_out
= f(last_in
)
154 base
.error('filter returned inappropriate nil')
158 elseif last_out
~= "" then
160 if last_in
then last_in
= "" end
164 last_out
= f(last_in
)
165 if last_out
== "" then
166 if last_in
== "" then
169 base
.error('filter returned ""')
171 elseif not last_out
then
173 base
.error('filter returned inappropriate nil')
185 -- creates a source that produces contents of several sources, one after the
186 -- other, as if they were concatenated
187 -- (thanks to Wim Couwenberg)
188 function source
.cat(...)
190 local src
= table.remove(arg
, 1)
193 local chunk
, err
= src()
194 if chunk
then return chunk
end
195 if err
then return nil, err
end
196 src
= table.remove(arg
, 1)
201 -----------------------------------------------------------------------------
203 -----------------------------------------------------------------------------
204 -- creates a sink that stores into a table
205 function sink
.table(t
)
207 local f
= function(chunk
, err
)
208 if chunk
then table.insert(t
, chunk
) end
214 -- turns a fancy sink into a simple sink
215 function sink
.simplify(snk
)
217 return function(chunk
, err
)
218 local ret
, err_or_new
= snk(chunk
, err
)
219 if not ret
then return nil, err_or_new
end
220 snk
= err_or_new
or snk
225 -- creates a file sink
226 function sink
.file(handle
, io_err
)
228 return function(chunk
, err
)
232 else return handle
:write(chunk
) end
234 else return sink
.error(io_err
or "unable to open file") end
237 -- creates a sink that discards data
238 local function null()
246 -- creates a sink that just returns an error
247 function sink
.error(err
)
253 -- chains a sink with a filter
254 function sink
.chain(f
, snk
)
255 base
.assert(f
and snk
)
256 return function(chunk
, err
)
258 local filtered
= f(chunk
)
259 local done
= chunk
and ""
261 local ret
, snkerr
= snk(filtered
, err
)
262 if not ret
then return nil, snkerr
end
263 if filtered
== done
then return 1 end
270 -----------------------------------------------------------------------------
272 -----------------------------------------------------------------------------
273 -- pumps one chunk from the source to the sink
274 function pump
.step(src
, snk
)
275 local chunk
, src_err
= src()
276 local ret
, snk_err
= snk(chunk
, src_err
)
277 if chunk
and ret
then return 1
278 else return nil, src_err
or snk_err
end
281 -- pumps all data from a source to a sink, using a step function
282 function pump
.all(src
, snk
, step
)
283 base
.assert(src
and snk
)
284 step
= step
or pump
.step
286 local ret
, err
= step(src
, snk
)
288 if err
then return nil, err