util-soc-imp-ltn12.lua /size: 8709 b    last modification: 2020-07-01 14:35
1-- original file : ltn12.lua
2-- for more into : see util-soc.lua
3
4local select, unpack = select, unpack
5local insert, remove = table.insert, table.remove
6local sub = string.sub
7
8local function report(fmt,first,...)
9    if logs then
10        report = logs and logs.reporter("ltn12")
11        report(fmt,first,...)
12    elseif fmt then
13        fmt = "ltn12: " .. fmt
14        if first then
15            print(format(fmt,first,...))
16        else
17            print(fmt)
18        end
19    end
20end
21
22local filter = { }
23local source = { }
24local sink   = { }
25local pump   = { }
26
27local ltn12 = {
28
29    _VERSION  = "LTN12 1.0.3",
30
31    BLOCKSIZE = 2048,
32
33    filter    = filter,
34    source    = source,
35    sink      = sink,
36    pump      = pump,
37
38    report    = report,
39
40}
41
42-- returns a high level filter that cycles a low-level filter
43
44function filter.cycle(low, ctx, extra)
45    if low then
46        return function(chunk)
47            return (low(ctx, chunk, extra))
48        end
49    end
50end
51
52-- chains a bunch of filters together
53
54function filter.chain(...)
55    local arg   = { ... }
56    local n     = select('#',...)
57    local top   = 1
58    local index = 1
59    local retry = ""
60    return function(chunk)
61        retry = chunk and retry
62        while true do
63            local action = arg[index]
64            if index == top then
65                chunk = action(chunk)
66                if chunk == "" or top == n then
67                    return chunk
68                elseif chunk then
69                    index = index + 1
70                else
71                    top   = top + 1
72                    index = top
73                end
74            else
75                chunk = action(chunk or "")
76                if chunk == "" then
77                    index = index - 1
78                    chunk = retry
79                elseif chunk then
80                    if index == n then
81                        return chunk
82                    else
83                        index = index + 1
84                    end
85                else
86                    report("error: filter returned inappropriate 'nil'")
87                    return
88                end
89            end
90        end
91    end
92end
93
94-- create an empty source
95
96local function empty()
97    return nil
98end
99
100function source.empty()
101    return empty
102end
103
104-- returns a source that just outputs an error
105
106local function sourceerror(err)
107    return function()
108        return nil, err
109    end
110end
111
112source.error = sourceerror
113
114-- creates a file source
115
116function source.file(handle, io_err)
117    if handle then
118        local blocksize = ltn12.BLOCKSIZE
119        return function()
120            local chunk = handle:read(blocksize)
121            if not chunk then
122                handle:close()
123            end
124            return chunk
125        end
126    else
127        return sourceerror(io_err or "unable to open file")
128    end
129end
130
131-- turns a fancy source into a simple source
132
133function source.simplify(src)
134    return function()
135        local chunk, err_or_new = src()
136        if err_or_new then
137            src = err_or_new
138        end
139        if chunk then
140            return chunk
141        else
142            return nil, err_or_new
143        end
144    end
145end
146
147-- creates string source
148
149function source.string(s)
150    if s then
151        local blocksize = ltn12.BLOCKSIZE
152        local i = 1
153        return function()
154            local nexti = i + blocksize
155            local chunk = sub(s, i, nexti - 1)
156            i = nexti
157            if chunk ~= "" then
158                return chunk
159            else
160                return nil
161            end
162        end
163    else return source.empty() end
164end
165
166-- creates rewindable source
167
168function source.rewind(src)
169    local t = { }
170    return function(chunk)
171        if chunk then
172            insert(t, chunk)
173        else
174            chunk = remove(t)
175            if chunk then
176                return chunk
177            else
178                return src()
179            end
180        end
181    end
182end
183
184-- chains a source with one or several filter(s)
185
186function source.chain(src, f, ...)
187    if ... then
188        f = filter.chain(f, ...)
189    end
190    local last_in  = ""
191    local last_out = ""
192    local state    = "feeding"
193    local err
194    return function()
195        if not last_out then
196            report("error: source is empty")
197            return
198        end
199        while true do
200            if state == "feeding" then
201                last_in, err = src()
202                if err then
203                    return nil, err
204                end
205                last_out = f(last_in)
206                if not last_out then
207                    if last_in then
208                        report("error: filter returned inappropriate 'nil'")
209                    end
210                    return nil
211                elseif last_out ~= "" then
212                    state = "eating"
213                    if last_in then
214                        last_in = ""
215                    end
216                    return last_out
217                end
218            else
219                last_out = f(last_in)
220                if last_out == "" then
221                    if last_in == "" then
222                        state = "feeding"
223                    else
224                        report("error: filter returned nothing")
225                        return
226                    end
227                elseif not last_out then
228                    if last_in then
229                        report("filter returned inappropriate 'nil'")
230                    end
231                    return nil
232                else
233                    return last_out
234                end
235            end
236        end
237    end
238end
239
240-- creates a source that produces contents of several sources, one after the
241-- other, as if they were concatenated
242
243function source.cat(...)
244    local arg = { ... }
245    local src = remove(arg,1)
246    return function()
247        while src do
248            local chunk, err = src()
249            if chunk then
250                return chunk
251            end
252            if err then
253                return nil, err
254            end
255            src = remove(arg,1)
256        end
257    end
258end
259
260-- creates a sink that stores into a table
261
262function sink.table(t)
263    if not t then
264        t = { }
265    end
266    local f = function(chunk, err)
267        if chunk then
268            insert(t, chunk)
269        end
270        return 1
271    end
272    return f, t
273end
274
275-- turns a fancy sink into a simple sink
276
277function sink.simplify(snk)
278    return function(chunk, err)
279        local ret, err_or_new = snk(chunk, err)
280        if not ret then
281            return nil, err_or_new
282        end
283        if err_or_new then
284            snk = err_or_new
285        end
286        return 1
287    end
288end
289
290-- creates a sink that discards data
291
292local function null()
293    return 1
294end
295
296function sink.null()
297    return null
298end
299
300-- creates a sink that just returns an error
301
302local function sinkerror(err)
303    return function()
304        return nil, err
305    end
306end
307
308sink.error = sinkerror
309
310-- creates a file sink
311
312function sink.file(handle, io_err)
313    if handle then
314        return function(chunk, err)
315            if not chunk then
316                handle:close()
317                return 1
318            else
319                return handle:write(chunk)
320            end
321        end
322    else
323        return sinkerror(io_err or "unable to open file")
324    end
325end
326
327-- chains a sink with one or several filter(s)
328
329function sink.chain(f, snk, ...)
330    if ... then
331        local args = { f, snk, ... }
332        snk = remove(args, #args)
333        f = filter.chain(unpack(args))
334    end
335    return function(chunk, err)
336        if chunk ~= "" then
337            local filtered = f(chunk)
338            local done     = chunk and ""
339            while true do
340                local ret, snkerr = snk(filtered, err)
341                if not ret then
342                    return nil, snkerr
343                end
344                if filtered == done then
345                    return 1
346                end
347                filtered = f(done)
348            end
349        else
350            return 1
351        end
352    end
353end
354
355-- pumps one chunk from the source to the sink
356
357function pump.step(src, snk)
358    local chunk, src_err = src()
359    local ret, snk_err = snk(chunk, src_err)
360    if chunk and ret then
361        return 1
362    else
363        return nil, src_err or snk_err
364    end
365end
366
367-- pumps all data from a source to a sink, using a step function
368
369function pump.all(src, snk, step)
370    if not step then
371        step = pump.step
372    end
373    while true do
374        local ret, err = step(src, snk)
375        if not ret then
376            if err then
377                return nil, err
378            else
379                return 1
380            end
381        end
382    end
383end
384
385package.loaded["ltn12"] = ltn12
386
387return ltn12
388