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

295 lines
8.0 KiB

  1. -----------------------------------------------------------------------------
  2. -- LTN12 - Filters, sources, sinks and pumps.
  3. -- LuaSocket toolkit.
  4. -- Author: Diego Nehab
  5. -- RCS ID: $Id: ltn12.lua,v 1.31 2006/04/03 04:45:42 diego Exp $
  6. -----------------------------------------------------------------------------
  7. -----------------------------------------------------------------------------
  8. -- Declare module
  9. -----------------------------------------------------------------------------
  10. local string = require("string")
  11. local table = require("table")
  12. local base = _G
  13. module("ltn12")
  14. filter = {}
  15. source = {}
  16. sink = {}
  17. pump = {}
  18. -- 2048 seems to be better in windows...
  19. BLOCKSIZE = 2048
  20. _VERSION = "LTN12 1.0.1"
  21. -----------------------------------------------------------------------------
  22. -- Filter stuff
  23. -----------------------------------------------------------------------------
  24. -- returns a high level filter that cycles a low-level filter
  25. function filter.cycle(low, ctx, extra)
  26. base.assert(low)
  27. return function(chunk)
  28. local ret
  29. ret, ctx = low(ctx, chunk, extra)
  30. return ret
  31. end
  32. end
  33. -- chains a bunch of filters together
  34. -- (thanks to Wim Couwenberg)
  35. function filter.chain(...)
  36. local arg = {...}
  37. local n = #arg
  38. local top, index = 1, 1
  39. local retry = ""
  40. return function(chunk)
  41. retry = chunk and retry
  42. while true do
  43. if index == top then
  44. chunk = arg[index](chunk)
  45. if chunk == "" or top == n then return chunk
  46. elseif chunk then index = index + 1
  47. else
  48. top = top+1
  49. index = top
  50. end
  51. else
  52. chunk = arg[index](chunk or "")
  53. if chunk == "" then
  54. index = index - 1
  55. chunk = retry
  56. elseif chunk then
  57. if index == n then return chunk
  58. else index = index + 1 end
  59. else base.error("filter returned inappropriate nil") end
  60. end
  61. end
  62. end
  63. end
  64. -----------------------------------------------------------------------------
  65. -- Source stuff
  66. -----------------------------------------------------------------------------
  67. -- create an empty source
  68. local function empty()
  69. return nil
  70. end
  71. function source.empty()
  72. return empty
  73. end
  74. -- returns a source that just outputs an error
  75. function source.error(err)
  76. return function()
  77. return nil, err
  78. end
  79. end
  80. -- creates a file source
  81. function source.file(handle, io_err)
  82. if handle then
  83. return function()
  84. local chunk = handle:read(BLOCKSIZE)
  85. if not chunk then handle:close() end
  86. return chunk
  87. end
  88. else return source.error(io_err or "unable to open file") end
  89. end
  90. -- turns a fancy source into a simple source
  91. function source.simplify(src)
  92. base.assert(src)
  93. return function()
  94. local chunk, err_or_new = src()
  95. src = err_or_new or src
  96. if not chunk then return nil, err_or_new
  97. else return chunk end
  98. end
  99. end
  100. -- creates string source
  101. function source.string(s)
  102. if s then
  103. local i = 1
  104. return function()
  105. local chunk = string.sub(s, i, i+BLOCKSIZE-1)
  106. i = i + BLOCKSIZE
  107. if chunk ~= "" then return chunk
  108. else return nil end
  109. end
  110. else return source.empty() end
  111. end
  112. -- creates rewindable source
  113. function source.rewind(src)
  114. base.assert(src)
  115. local t = {}
  116. return function(chunk)
  117. if not chunk then
  118. chunk = table.remove(t)
  119. if not chunk then return src()
  120. else return chunk end
  121. else
  122. table.insert(t, chunk)
  123. end
  124. end
  125. end
  126. function source.chain(src, f)
  127. base.assert(src and f)
  128. local last_in, last_out = "", ""
  129. local state = "feeding"
  130. local err
  131. return function()
  132. if not last_out then
  133. base.error('source is empty!', 2)
  134. end
  135. while true do
  136. if state == "feeding" then
  137. last_in, err = src()
  138. if err then return nil, err end
  139. last_out = f(last_in)
  140. if not last_out then
  141. if last_in then
  142. base.error('filter returned inappropriate nil')
  143. else
  144. return nil
  145. end
  146. elseif last_out ~= "" then
  147. state = "eating"
  148. if last_in then last_in = "" end
  149. return last_out
  150. end
  151. else
  152. last_out = f(last_in)
  153. if last_out == "" then
  154. if last_in == "" then
  155. state = "feeding"
  156. else
  157. base.error('filter returned ""')
  158. end
  159. elseif not last_out then
  160. if last_in then
  161. base.error('filter returned inappropriate nil')
  162. else
  163. return nil
  164. end
  165. else
  166. return last_out
  167. end
  168. end
  169. end
  170. end
  171. end
  172. -- creates a source that produces contents of several sources, one after the
  173. -- other, as if they were concatenated
  174. -- (thanks to Wim Couwenberg)
  175. function source.cat(...)
  176. local arg = {...}
  177. local src = table.remove(arg, 1)
  178. return function()
  179. while src do
  180. local chunk, err = src()
  181. if chunk then return chunk end
  182. if err then return nil, err end
  183. src = table.remove(arg, 1)
  184. end
  185. end
  186. end
  187. -----------------------------------------------------------------------------
  188. -- Sink stuff
  189. -----------------------------------------------------------------------------
  190. -- creates a sink that stores into a table
  191. function sink.table(t)
  192. t = t or {}
  193. local f = function(chunk, err)
  194. if chunk then table.insert(t, chunk) end
  195. return 1
  196. end
  197. return f, t
  198. end
  199. -- turns a fancy sink into a simple sink
  200. function sink.simplify(snk)
  201. base.assert(snk)
  202. return function(chunk, err)
  203. local ret, err_or_new = snk(chunk, err)
  204. if not ret then return nil, err_or_new end
  205. snk = err_or_new or snk
  206. return 1
  207. end
  208. end
  209. -- creates a file sink
  210. function sink.file(handle, io_err)
  211. if handle then
  212. return function(chunk, err)
  213. if not chunk then
  214. handle:close()
  215. return 1
  216. else return handle:write(chunk) end
  217. end
  218. else return sink.error(io_err or "unable to open file") end
  219. end
  220. -- creates a sink that discards data
  221. local function null()
  222. return 1
  223. end
  224. function sink.null()
  225. return null
  226. end
  227. -- creates a sink that just returns an error
  228. function sink.error(err)
  229. return function()
  230. return nil, err
  231. end
  232. end
  233. -- chains a sink with a filter
  234. function sink.chain(f, snk)
  235. base.assert(f and snk)
  236. return function(chunk, err)
  237. if chunk ~= "" then
  238. local filtered = f(chunk)
  239. local done = chunk and ""
  240. while true do
  241. local ret, snkerr = snk(filtered, err)
  242. if not ret then return nil, snkerr end
  243. if filtered == done then return 1 end
  244. filtered = f(done)
  245. end
  246. else return 1 end
  247. end
  248. end
  249. -----------------------------------------------------------------------------
  250. -- Pump stuff
  251. -----------------------------------------------------------------------------
  252. -- pumps one chunk from the source to the sink
  253. function pump.step(src, snk)
  254. local chunk, src_err = src()
  255. local ret, snk_err = snk(chunk, src_err)
  256. if chunk and ret then return 1
  257. else return nil, src_err or snk_err end
  258. end
  259. -- pumps all data from a source to a sink, using a step function
  260. function pump.all(src, snk, step)
  261. base.assert(src and snk)
  262. step = step or pump.step
  263. while true do
  264. local ret, err = step(src, snk)
  265. if not ret then
  266. if err then return nil, err
  267. else return 1 end
  268. end
  269. end
  270. end