diff options
| author | Diego Nehab <diego@tecgraf.puc-rio.br> | 2007-10-11 21:16:28 +0000 |
|---|---|---|
| committer | Diego Nehab <diego@tecgraf.puc-rio.br> | 2007-10-11 21:16:28 +0000 |
| commit | 52ac60af8132ae7e42151d3012a9607d7cadaf95 (patch) | |
| tree | 95085f9f535d20b6686399af1ba284bb74ac89d3 | |
| parent | e394956cde629e24ecdd285c4c13c206891fcec4 (diff) | |
| download | luasocket-52ac60af8132ae7e42151d3012a9607d7cadaf95.tar.gz luasocket-52ac60af8132ae7e42151d3012a9607d7cadaf95.tar.bz2 luasocket-52ac60af8132ae7e42151d3012a9607d7cadaf95.zip | |
Tested each sample.
| -rw-r--r-- | gem/ex1.lua | 4 | ||||
| -rw-r--r-- | gem/ex10.lua | 17 | ||||
| -rw-r--r-- | gem/ex11.lua | 7 | ||||
| -rw-r--r-- | gem/ex12.lua | 34 | ||||
| -rw-r--r-- | gem/ex2.lua | 11 | ||||
| -rw-r--r-- | gem/ex3.lua | 15 | ||||
| -rw-r--r-- | gem/ex4.lua | 5 | ||||
| -rw-r--r-- | gem/ex5.lua | 15 | ||||
| -rw-r--r-- | gem/ex6.lua | 14 | ||||
| -rw-r--r-- | gem/ex7.lua | 16 | ||||
| -rw-r--r-- | gem/ex8.lua | 5 | ||||
| -rw-r--r-- | gem/ex9.lua | 3 | ||||
| -rw-r--r-- | gem/gem.c | 54 | ||||
| -rw-r--r-- | gem/gt.b64 | 206 | ||||
| -rw-r--r-- | gem/input.bin | bin | 0 -> 11732 bytes | |||
| -rw-r--r-- | gem/ltn012.tex | 355 | ||||
| -rw-r--r-- | gem/luasocket.png | bin | 0 -> 11732 bytes | |||
| -rw-r--r-- | gem/makefile | 9 | ||||
| -rw-r--r-- | gem/t1.lua | 25 | ||||
| -rw-r--r-- | gem/t1lf.txt | 5 | ||||
| -rw-r--r-- | gem/t2.lua | 36 | ||||
| -rw-r--r-- | gem/t2.txt | 4 | ||||
| -rw-r--r-- | gem/t2gt.qp | 5 | ||||
| -rw-r--r-- | gem/t3.lua | 25 | ||||
| -rw-r--r-- | gem/t4.lua | 10 | ||||
| -rw-r--r-- | gem/t5.lua | 30 | ||||
| -rw-r--r-- | gem/test.lua | 46 |
27 files changed, 791 insertions, 165 deletions
diff --git a/gem/ex1.lua b/gem/ex1.lua new file mode 100644 index 0000000..327a542 --- /dev/null +++ b/gem/ex1.lua | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | local CRLF = "\013\010" | ||
| 2 | local input = source.chain(source.file(io.stdin), normalize(CRLF)) | ||
| 3 | local output = sink.file(io.stdout) | ||
| 4 | pump.all(input, output) | ||
diff --git a/gem/ex10.lua b/gem/ex10.lua new file mode 100644 index 0000000..2b1b98f --- /dev/null +++ b/gem/ex10.lua | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | function pump.step(src, snk) | ||
| 2 | local chunk, src_err = src() | ||
| 3 | local ret, snk_err = snk(chunk, src_err) | ||
| 4 | if chunk and ret then return 1 | ||
| 5 | else return nil, src_err or snk_err end | ||
| 6 | end | ||
| 7 | |||
| 8 | function pump.all(src, snk, step) | ||
| 9 | step = step or pump.step | ||
| 10 | while true do | ||
| 11 | local ret, err = step(src, snk) | ||
| 12 | if not ret then | ||
| 13 | if err then return nil, err | ||
| 14 | else return 1 end | ||
| 15 | end | ||
| 16 | end | ||
| 17 | end | ||
diff --git a/gem/ex11.lua b/gem/ex11.lua new file mode 100644 index 0000000..1cbf01f --- /dev/null +++ b/gem/ex11.lua | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | local input = source.chain( | ||
| 2 | source.file(io.open("input.bin", "rb")), | ||
| 3 | encode("base64")) | ||
| 4 | local output = sink.chain( | ||
| 5 | wrap(76), | ||
| 6 | sink.file(io.open("output.b64", "w"))) | ||
| 7 | pump.all(input, output) | ||
diff --git a/gem/ex12.lua b/gem/ex12.lua new file mode 100644 index 0000000..de17d76 --- /dev/null +++ b/gem/ex12.lua | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | local smtp = require"socket.smtp" | ||
| 2 | local mime = require"mime" | ||
| 3 | local ltn12 = require"ltn12" | ||
| 4 | |||
| 5 | CRLF = "\013\010" | ||
| 6 | |||
| 7 | local message = smtp.message{ | ||
| 8 | headers = { | ||
| 9 | from = "Sicrano <sicrano@example.com>", | ||
| 10 | to = "Fulano <fulano@example.com>", | ||
| 11 | subject = "A message with an attachment"}, | ||
| 12 | body = { | ||
| 13 | preamble = "Hope you can see the attachment" .. CRLF, | ||
| 14 | [1] = { | ||
| 15 | body = "Here is our logo" .. CRLF}, | ||
| 16 | [2] = { | ||
| 17 | headers = { | ||
| 18 | ["content-type"] = 'image/png; name="luasocket.png"', | ||
| 19 | ["content-disposition"] = | ||
| 20 | 'attachment; filename="luasocket.png"', | ||
| 21 | ["content-description"] = 'LuaSocket logo', | ||
| 22 | ["content-transfer-encoding"] = "BASE64"}, | ||
| 23 | body = ltn12.source.chain( | ||
| 24 | ltn12.source.file(io.open("luasocket.png", "rb")), | ||
| 25 | ltn12.filter.chain( | ||
| 26 | mime.encode("base64"), | ||
| 27 | mime.wrap()))}}} | ||
| 28 | |||
| 29 | assert(smtp.send{ | ||
| 30 | rcpt = "<diego@cs.princeton.edu>", | ||
| 31 | from = "<diego@cs.princeton.edu>", | ||
| 32 | server = "localhost", | ||
| 33 | port = 2525, | ||
| 34 | source = message}) | ||
diff --git a/gem/ex2.lua b/gem/ex2.lua new file mode 100644 index 0000000..94bde66 --- /dev/null +++ b/gem/ex2.lua | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | function filter.cycle(lowlevel, context, extra) | ||
| 2 | return function(chunk) | ||
| 3 | local ret | ||
| 4 | ret, context = lowlevel(context, chunk, extra) | ||
| 5 | return ret | ||
| 6 | end | ||
| 7 | end | ||
| 8 | |||
| 9 | function normalize(marker) | ||
| 10 | return filter.cycle(eol, 0, marker) | ||
| 11 | end | ||
diff --git a/gem/ex3.lua b/gem/ex3.lua new file mode 100644 index 0000000..a43fefa --- /dev/null +++ b/gem/ex3.lua | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | local function chainpair(f1, f2) | ||
| 2 | return function(chunk) | ||
| 3 | local ret = f2(f1(chunk)) | ||
| 4 | if chunk then return ret | ||
| 5 | else return (ret or "") .. (f2() or "") end | ||
| 6 | end | ||
| 7 | end | ||
| 8 | |||
| 9 | function filter.chain(...) | ||
| 10 | local f = select(1, ...) | ||
| 11 | for i = 2, select('#', ...) do | ||
| 12 | f = chainpair(f, select(i, ...)) | ||
| 13 | end | ||
| 14 | return f | ||
| 15 | end | ||
diff --git a/gem/ex4.lua b/gem/ex4.lua new file mode 100644 index 0000000..c670e0e --- /dev/null +++ b/gem/ex4.lua | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | local qp = filter.chain(normalize(CRLF), encode("quoted-printable"), | ||
| 2 | wrap("quoted-printable")) | ||
| 3 | local input = source.chain(source.file(io.stdin), qp) | ||
| 4 | local output = sink.file(io.stdout) | ||
| 5 | pump.all(input, output) | ||
diff --git a/gem/ex5.lua b/gem/ex5.lua new file mode 100644 index 0000000..196b30a --- /dev/null +++ b/gem/ex5.lua | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | function source.empty(err) | ||
| 2 | return function() | ||
| 3 | return nil, err | ||
| 4 | end | ||
| 5 | end | ||
| 6 | |||
| 7 | function source.file(handle, io_err) | ||
| 8 | if handle then | ||
| 9 | return function() | ||
| 10 | local chunk = handle:read(20) | ||
| 11 | if not chunk then handle:close() end | ||
| 12 | return chunk | ||
| 13 | end | ||
| 14 | else return source.empty(io_err or "unable to open file") end | ||
| 15 | end | ||
diff --git a/gem/ex6.lua b/gem/ex6.lua new file mode 100644 index 0000000..a3fdca0 --- /dev/null +++ b/gem/ex6.lua | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | function source.chain(src, f) | ||
| 2 | return function() | ||
| 3 | if not src then | ||
| 4 | return nil | ||
| 5 | end | ||
| 6 | local chunk, err = src() | ||
| 7 | if not chunk then | ||
| 8 | src = nil | ||
| 9 | return f(nil) | ||
| 10 | else | ||
| 11 | return f(chunk) | ||
| 12 | end | ||
| 13 | end | ||
| 14 | end | ||
diff --git a/gem/ex7.lua b/gem/ex7.lua new file mode 100644 index 0000000..c766988 --- /dev/null +++ b/gem/ex7.lua | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | function sink.table(t) | ||
| 2 | t = t or {} | ||
| 3 | local f = function(chunk, err) | ||
| 4 | if chunk then table.insert(t, chunk) end | ||
| 5 | return 1 | ||
| 6 | end | ||
| 7 | return f, t | ||
| 8 | end | ||
| 9 | |||
| 10 | local function null() | ||
| 11 | return 1 | ||
| 12 | end | ||
| 13 | |||
| 14 | function sink.null() | ||
| 15 | return null | ||
| 16 | end | ||
diff --git a/gem/ex8.lua b/gem/ex8.lua new file mode 100644 index 0000000..81e288c --- /dev/null +++ b/gem/ex8.lua | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | local input = source.file(io.stdin) | ||
| 2 | local output, t = sink.table() | ||
| 3 | output = sink.chain(normalize(CRLF), output) | ||
| 4 | pump.all(input, output) | ||
| 5 | io.write(table.concat(t)) | ||
diff --git a/gem/ex9.lua b/gem/ex9.lua new file mode 100644 index 0000000..b857698 --- /dev/null +++ b/gem/ex9.lua | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | for chunk in source.file(io.stdin) do | ||
| 2 | io.write(chunk) | ||
| 3 | end | ||
diff --git a/gem/gem.c b/gem/gem.c new file mode 100644 index 0000000..976f74d --- /dev/null +++ b/gem/gem.c | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | #include "lua.h" | ||
| 2 | #include "lauxlib.h" | ||
| 3 | |||
| 4 | #define CR '\xD' | ||
| 5 | #define LF '\xA' | ||
| 6 | #define CRLF "\xD\xA" | ||
| 7 | |||
| 8 | #define candidate(c) (c == CR || c == LF) | ||
| 9 | static int pushchar(int c, int last, const char *marker, | ||
| 10 | luaL_Buffer *buffer) { | ||
| 11 | if (candidate(c)) { | ||
| 12 | if (candidate(last)) { | ||
| 13 | if (c == last) | ||
| 14 | luaL_addstring(buffer, marker); | ||
| 15 | return 0; | ||
| 16 | } else { | ||
| 17 | luaL_addstring(buffer, marker); | ||
| 18 | return c; | ||
| 19 | } | ||
| 20 | } else { | ||
| 21 | luaL_putchar(buffer, c); | ||
| 22 | return 0; | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | static int eol(lua_State *L) { | ||
| 27 | int context = luaL_checkint(L, 1); | ||
| 28 | size_t isize = 0; | ||
| 29 | const char *input = luaL_optlstring(L, 2, NULL, &isize); | ||
| 30 | const char *last = input + isize; | ||
| 31 | const char *marker = luaL_optstring(L, 3, CRLF); | ||
| 32 | luaL_Buffer buffer; | ||
| 33 | luaL_buffinit(L, &buffer); | ||
| 34 | if (!input) { | ||
| 35 | lua_pushnil(L); | ||
| 36 | lua_pushnumber(L, 0); | ||
| 37 | return 2; | ||
| 38 | } | ||
| 39 | while (input < last) | ||
| 40 | context = pushchar(*input++, context, marker, &buffer); | ||
| 41 | luaL_pushresult(&buffer); | ||
| 42 | lua_pushnumber(L, context); | ||
| 43 | return 2; | ||
| 44 | } | ||
| 45 | |||
| 46 | static luaL_reg func[] = { | ||
| 47 | { "eol", eol }, | ||
| 48 | { NULL, NULL } | ||
| 49 | }; | ||
| 50 | |||
| 51 | int luaopen_gem(lua_State *L) { | ||
| 52 | luaL_openlib(L, "gem", func, 0); | ||
| 53 | return 0; | ||
| 54 | } | ||
diff --git a/gem/gt.b64 b/gem/gt.b64 new file mode 100644 index 0000000..a74c0b3 --- /dev/null +++ b/gem/gt.b64 | |||
| @@ -0,0 +1,206 @@ | |||
| 1 | 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 | ||
| 197 | u9xa3ooEUMejVmJUFEVRFJEuK8Zjs9lUY1ZH/9yQ1bP3goa0Ku7sdntOTk5+fn59fX1CQsIdd9wR | ||
| 198 | FxenfbX7svDdyuYj3CwZ4A7qNk0MQARTqmfNAYDNZlu7dq3FYpkwYUJSUpI4ckKrh2hnnIvBw9O7 | ||
| 199 | tPeD2ykuIm8rMTHxoYceEjsp7SMuEkIVxb27KPtHA3HX9gTDPXv27MmTJ8+fP2+1WtPS0jIyMnqw | ||
| 200 | uXdRIgU0NzdXVlYWFhaOHz8+ISFBZXoqu+uyQupNgr5bAVpwd2oCgCRJw4YNGz58uKIo586dcxED | ||
| 201 | R44cqampGTRoUGBgoMViCQ4OFhsLLaIZY4qiUEpFjSN1J7hjxw5ZlgkhgYGBqampLj1RVaA+EP6d | ||
| 202 | oKJ/dXABWg4LTkah5d0iSe3YsWMOh8Nms8myvHLlyujoaDGR29vb169f39LSIqwI4eHhK1euBKdh | ||
| 203 | ubq6uqioaMiQIZGRkULegJvZqh93grcEAQRop7N2q6xlVoyx1tZWq9U6YMAAbSDU8ePHEULiANOg | ||
| 204 | oKDY2FithHCRFv0y0z3BLUQAT6C6d7TaIfiAR5c9bZcBA/0C/wEEEKDtZ6duHy1a3Wtk37LwH0OA | ||
| 205 | /1fhphjjfgTf4f8C4VLHz/5KLxoAAAA8dEVYdGNvbW1lbnQAIEltYWdlIGdlbmVyYXRlZCBieSBH | ||
| 206 | TlUgR2hvc3RzY3JpcHQgKGRldmljZT1wbm1yYXcpCvqLFvMAAAAASUVORK5CYII= | ||
diff --git a/gem/input.bin b/gem/input.bin new file mode 100644 index 0000000..d24a954 --- /dev/null +++ b/gem/input.bin | |||
| Binary files differ | |||
diff --git a/gem/ltn012.tex b/gem/ltn012.tex index 0f81b86..8eccd46 100644 --- a/gem/ltn012.tex +++ b/gem/ltn012.tex | |||
| @@ -6,7 +6,10 @@ | |||
| 6 | \DefineVerbatimEnvironment{mime}{Verbatim}{fontsize=\small,commandchars=\$\#\%} | 6 | \DefineVerbatimEnvironment{mime}{Verbatim}{fontsize=\small,commandchars=\$\#\%} |
| 7 | \newcommand{\stick}[1]{\vbox{\setlength{\parskip}{0pt}#1}} | 7 | \newcommand{\stick}[1]{\vbox{\setlength{\parskip}{0pt}#1}} |
| 8 | \newcommand{\bl}{\ensuremath{\mathtt{\backslash}}} | 8 | \newcommand{\bl}{\ensuremath{\mathtt{\backslash}}} |
| 9 | 9 | \newcommand{\CR}{\texttt{CR}} | |
| 10 | \newcommand{\LF}{\texttt{LF}} | ||
| 11 | \newcommand{\CRLF}{\texttt{CR~LF}} | ||
| 12 | \newcommand{\nil}{\texttt{nil}} | ||
| 10 | 13 | ||
| 11 | \title{Filters, sources, sinks, and pumps\\ | 14 | \title{Filters, sources, sinks, and pumps\\ |
| 12 | {\large or Functional programming for the rest of us}} | 15 | {\large or Functional programming for the rest of us}} |
| @@ -17,30 +20,31 @@ | |||
| 17 | \maketitle | 20 | \maketitle |
| 18 | 21 | ||
| 19 | \begin{abstract} | 22 | \begin{abstract} |
| 20 | Certain data processing operations can be implemented in the | 23 | Certain data processing operations can be implemented in the |
| 21 | form of filters. A filter is a function that can process data | 24 | form of filters. A filter is a function that can process |
| 22 | received in consecutive function calls, returning partial | 25 | data received in consecutive invocations, returning partial |
| 23 | results after each invocation. Examples of operations that can be | 26 | results each time it is called. Examples of operations that |
| 24 | implemented as filters include the end-of-line normalization | 27 | can be implemented as filters include the end-of-line |
| 25 | for text, Base64 and Quoted-Printable transfer content | 28 | normalization for text, Base64 and Quoted-Printable transfer |
| 26 | encodings, the breaking of text into lines, SMTP dot-stuffing, | 29 | content encodings, the breaking of text into lines, SMTP |
| 27 | and there are many others. Filters become even | 30 | dot-stuffing, and there are many others. Filters become |
| 28 | more powerful when we allow them to be chained together to | 31 | even more powerful when we allow them to be chained together |
| 29 | create composite filters. In this context, filters can be seen | 32 | to create composite filters. In this context, filters can be |
| 30 | as the middle links in a chain of data transformations. Sources an sinks | 33 | seen as the internal links in a chain of data transformations. |
| 31 | are the corresponding end points of these chains. A source | 34 | Sources and sinks are the corresponding end points in these |
| 32 | is a function that produces data, chunk by chunk, and a sink | 35 | chains. A source is a function that produces data, chunk by |
| 33 | is a function that takes data, chunk by chunk. In this | 36 | chunk, and a sink is a function that takes data, chunk by |
| 34 | article, we describe the design of an elegant interface for filters, | 37 | chunk. Finally, pumps are procedures that actively drive |
| 35 | sources, sinks, and chaining, and illustrate each step | 38 | data from a source to a sink, and indirectly through all |
| 36 | with concrete examples. | 39 | intervening filters. In this article, we describe the design of an |
| 40 | elegant interface for filters, sources, sinks, chains, and | ||
| 41 | pumps, and we illustrate each step with concrete examples. | ||
| 37 | \end{abstract} | 42 | \end{abstract} |
| 38 | 43 | ||
| 39 | |||
| 40 | \section{Introduction} | 44 | \section{Introduction} |
| 41 | 45 | ||
| 42 | Within the realm of networking applications, we are often | 46 | Within the realm of networking applications, we are often |
| 43 | required apply transformations to streams of data. Examples | 47 | required to apply transformations to streams of data. Examples |
| 44 | include the end-of-line normalization for text, Base64 and | 48 | include the end-of-line normalization for text, Base64 and |
| 45 | Quoted-Printable transfer content encodings, breaking text | 49 | Quoted-Printable transfer content encodings, breaking text |
| 46 | into lines with a maximum number of columns, SMTP | 50 | into lines with a maximum number of columns, SMTP |
| @@ -50,11 +54,10 @@ transfer coding, and the list goes on. | |||
| 50 | Many complex tasks require a combination of two or more such | 54 | Many complex tasks require a combination of two or more such |
| 51 | transformations, and therefore a general mechanism for | 55 | transformations, and therefore a general mechanism for |
| 52 | promoting reuse is desirable. In the process of designing | 56 | promoting reuse is desirable. In the process of designing |
| 53 | \texttt{LuaSocket~2.0}, David Burgess and I were forced to deal with | 57 | \texttt{LuaSocket~2.0}, we repeatedly faced this problem. |
| 54 | this problem. The solution we reached proved to be very | 58 | The solution we reached proved to be very general and |
| 55 | general and convenient. It is based on the concepts of | 59 | convenient. It is based on the concepts of filters, sources, |
| 56 | filters, sources, sinks, and pumps, which we introduce | 60 | sinks, and pumps, which we introduce below. |
| 57 | below. | ||
| 58 | 61 | ||
| 59 | \emph{Filters} are functions that can be repeatedly invoked | 62 | \emph{Filters} are functions that can be repeatedly invoked |
| 60 | with chunks of input, successively returning processed | 63 | with chunks of input, successively returning processed |
| @@ -62,34 +65,33 @@ chunks of output. More importantly, the result of | |||
| 62 | concatenating all the output chunks must be the same as the | 65 | concatenating all the output chunks must be the same as the |
| 63 | result of applying the filter to the concatenation of all | 66 | result of applying the filter to the concatenation of all |
| 64 | input chunks. In fancier language, filters \emph{commute} | 67 | input chunks. In fancier language, filters \emph{commute} |
| 65 | with the concatenation operator. As a result, chunk | 68 | with the concatenation operator. More importantly, filters |
| 66 | boundaries are irrelevant: filters correctly handle input | 69 | must handle input data correctly no matter how the stream |
| 67 | data no matter how it is split. | 70 | has been split into chunks. |
| 68 | 71 | ||
| 69 | A \emph{chain} transparently combines the effect of one or | 72 | A \emph{chain} is a function that transparently combines the |
| 70 | more filters. The interface of a chain is | 73 | effect of one or more filters. The interface of a chain is |
| 71 | indistinguishable from the interface of its components. | 74 | indistinguishable from the interface of its component |
| 72 | This allows a chained filter to be used wherever an atomic | 75 | filters. This allows a chained filter to be used wherever |
| 73 | filter is expected. In particular, chains can be | 76 | an atomic filter is accepted. In particular, chains can be |
| 74 | themselves chained to create arbitrarily complex operations. | 77 | themselves chained to create arbitrarily complex operations. |
| 75 | 78 | ||
| 76 | Filters can be seen as internal nodes in a network through | 79 | Filters can be seen as internal nodes in a network through |
| 77 | which data will flow, potentially being transformed many | 80 | which data will flow, potentially being transformed many |
| 78 | times along its way. Chains connect these nodes together. | 81 | times along the way. Chains connect these nodes together. |
| 79 | To complete the picture, we need \emph{sources} and | 82 | The initial and final nodes of the network are |
| 80 | \emph{sinks}. These are the initial and final nodes of the | 83 | \emph{sources} and \emph{sinks}, respectively. Less |
| 81 | network, respectively. Less abstractly, a source is a | 84 | abstractly, a source is a function that produces new data |
| 82 | function that produces new data every time it is called. | 85 | every time it is invoked. Conversely, sinks are functions |
| 83 | Conversely, sinks are functions that give a final | 86 | that give a final destination to the data they receive. |
| 84 | destination to the data they receive. Naturally, sources | 87 | Naturally, sources and sinks can also be chained with |
| 85 | and sinks can also be chained with filters to produce | 88 | filters to produce filtered sources and sinks. |
| 86 | filtered sources and sinks. | ||
| 87 | 89 | ||
| 88 | Finally, filters, chains, sources, and sinks are all passive | 90 | Finally, filters, chains, sources, and sinks are all passive |
| 89 | entities: they must be repeatedly invoked in order for | 91 | entities: they must be repeatedly invoked in order for |
| 90 | anything to happen. \emph{Pumps} provide the driving force | 92 | anything to happen. \emph{Pumps} provide the driving force |
| 91 | that pushes data through the network, from a source to a | 93 | that pushes data through the network, from a source to a |
| 92 | sink. | 94 | sink, and indirectly through all intervening filters. |
| 93 | 95 | ||
| 94 | In the following sections, we start with a simplified | 96 | In the following sections, we start with a simplified |
| 95 | interface, which we later refine. The evolution we present | 97 | interface, which we later refine. The evolution we present |
| @@ -99,27 +101,28 @@ concepts within our application domain. | |||
| 99 | 101 | ||
| 100 | \subsection{A simple example} | 102 | \subsection{A simple example} |
| 101 | 103 | ||
| 102 | Let us use the end-of-line normalization of text as an | 104 | The end-of-line normalization of text is a good |
| 103 | example to motivate our initial filter interface. | 105 | example to motivate our initial filter interface. |
| 104 | Assume we are given text in an unknown end-of-line | 106 | Assume we are given text in an unknown end-of-line |
| 105 | convention (including possibly mixed conventions) out of the | 107 | convention (including possibly mixed conventions) out of the |
| 106 | commonly found Unix (LF), Mac OS (CR), and DOS (CRLF) | 108 | commonly found Unix (\LF), Mac OS (\CR), and |
| 107 | conventions. We would like to be able to write code like the | 109 | DOS (\CRLF) conventions. We would like to be able to |
| 108 | following: | 110 | use the folowing code to normalize the end-of-line markers: |
| 109 | \begin{quote} | 111 | \begin{quote} |
| 110 | \begin{lua} | 112 | \begin{lua} |
| 111 | @stick# | 113 | @stick# |
| 112 | local in = source.chain(source.file(io.stdin), normalize("\r\n")) | 114 | local CRLF = "\013\010" |
| 113 | local out = sink.file(io.stdout) | 115 | local input = source.chain(source.file(io.stdin), normalize(CRLF)) |
| 114 | pump.all(in, out) | 116 | local output = sink.file(io.stdout) |
| 117 | pump.all(input, output) | ||
| 115 | % | 118 | % |
| 116 | \end{lua} | 119 | \end{lua} |
| 117 | \end{quote} | 120 | \end{quote} |
| 118 | 121 | ||
| 119 | This program should read data from the standard input stream | 122 | This program should read data from the standard input stream |
| 120 | and normalize the end-of-line markers to the canonic CRLF | 123 | and normalize the end-of-line markers to the canonic |
| 121 | marker, as defined by the MIME standard. Finally, the | 124 | \CRLF\ marker, as defined by the MIME standard. |
| 122 | normalized text should be sent to the standard output | 125 | Finally, the normalized text should be sent to the standard output |
| 123 | stream. We use a \emph{file source} that produces data from | 126 | stream. We use a \emph{file source} that produces data from |
| 124 | standard input, and chain it with a filter that normalizes | 127 | standard input, and chain it with a filter that normalizes |
| 125 | the data. The pump then repeatedly obtains data from the | 128 | the data. The pump then repeatedly obtains data from the |
| @@ -127,27 +130,28 @@ source, and passes it to the \emph{file sink}, which sends | |||
| 127 | it to the standard output. | 130 | it to the standard output. |
| 128 | 131 | ||
| 129 | In the code above, the \texttt{normalize} \emph{factory} is a | 132 | In the code above, the \texttt{normalize} \emph{factory} is a |
| 130 | function that creates our normalization filter. This filter | 133 | function that creates our normalization filter, which |
| 131 | will replace any end-of-line marker with the canonic | 134 | replaces any end-of-line marker with the canonic marker. |
| 132 | `\verb|\r\n|' marker. The initial filter interface is | 135 | The initial filter interface is |
| 133 | trivial: a filter function receives a chunk of input data, | 136 | trivial: a filter function receives a chunk of input data, |
| 134 | and returns a chunk of processed data. When there are no | 137 | and returns a chunk of processed data. When there are no |
| 135 | more input data left, the caller notifies the filter by invoking | 138 | more input data left, the caller notifies the filter by invoking |
| 136 | it with a \texttt{nil} chunk. The filter responds by returning | 139 | it with a \nil\ chunk. The filter responds by returning |
| 137 | the final chunk of processed data. | 140 | the final chunk of processed data (which could of course be |
| 141 | the empty string). | ||
| 138 | 142 | ||
| 139 | Although the interface is extremely simple, the | 143 | Although the interface is extremely simple, the |
| 140 | implementation is not so obvious. A normalization filter | 144 | implementation is not so obvious. A normalization filter |
| 141 | respecting this interface needs to keep some kind of context | 145 | respecting this interface needs to keep some kind of context |
| 142 | between calls. This is because a chunk boundary may lie between | 146 | between calls. This is because a chunk boundary may lie between |
| 143 | the CR and LF characters marking the end of a line. This | 147 | the \CR\ and \LF\ characters marking the end of a single line. This |
| 144 | need for contextual storage motivates the use of | 148 | need for contextual storage motivates the use of |
| 145 | factories: each time the factory is invoked, it returns a | 149 | factories: each time the factory is invoked, it returns a |
| 146 | filter with its own context so that we can have several | 150 | filter with its own context so that we can have several |
| 147 | independent filters being used at the same time. For | 151 | independent filters being used at the same time. For |
| 148 | efficiency reasons, we must avoid the obvious solution of | 152 | efficiency reasons, we must avoid the obvious solution of |
| 149 | concatenating all the input into the context before | 153 | concatenating all the input into the context before |
| 150 | producing any output. | 154 | producing any output chunks. |
| 151 | 155 | ||
| 152 | To that end, we break the implementation into two parts: | 156 | To that end, we break the implementation into two parts: |
| 153 | a low-level filter, and a factory of high-level filters. The | 157 | a low-level filter, and a factory of high-level filters. The |
| @@ -167,10 +171,10 @@ end-of-line normalization filters: | |||
| 167 | \begin{quote} | 171 | \begin{quote} |
| 168 | \begin{lua} | 172 | \begin{lua} |
| 169 | @stick# | 173 | @stick# |
| 170 | function filter.cycle(low, ctx, extra) | 174 | function filter.cycle(lowlevel, context, extra) |
| 171 | return function(chunk) | 175 | return function(chunk) |
| 172 | local ret | 176 | local ret |
| 173 | ret, ctx = low(ctx, chunk, extra) | 177 | ret, context = lowlevel(context, chunk, extra) |
| 174 | return ret | 178 | return ret |
| 175 | end | 179 | end |
| 176 | end | 180 | end |
| @@ -178,27 +182,30 @@ end | |||
| 178 | 182 | ||
| 179 | @stick# | 183 | @stick# |
| 180 | function normalize(marker) | 184 | function normalize(marker) |
| 181 | return cycle(eol, 0, marker) | 185 | return filter.cycle(eol, 0, marker) |
| 182 | end | 186 | end |
| 183 | % | 187 | % |
| 184 | \end{lua} | 188 | \end{lua} |
| 185 | \end{quote} | 189 | \end{quote} |
| 186 | 190 | ||
| 187 | The \texttt{normalize} factory simply calls a more generic | 191 | The \texttt{normalize} factory simply calls a more generic |
| 188 | factory, the \texttt{cycle} factory. This factory receives a | 192 | factory, the \texttt{cycle}~factory, passing the low-level |
| 193 | filter~\texttt{eol}. The \texttt{cycle}~factory receives a | ||
| 189 | low-level filter, an initial context, and an extra | 194 | low-level filter, an initial context, and an extra |
| 190 | parameter, and returns a new high-level filter. Each time | 195 | parameter, and returns a new high-level filter. Each time |
| 191 | the high-level filer is passed a new chunk, it invokes the | 196 | the high-level filer is passed a new chunk, it invokes the |
| 192 | low-level filter with the previous context, the new chunk, | 197 | low-level filter with the previous context, the new chunk, |
| 193 | and the extra argument. It is the low-level filter that | 198 | and the extra argument. It is the low-level filter that |
| 194 | does all the work, producing the chunk of processed data and | 199 | does all the work, producing the chunk of processed data and |
| 195 | a new context. The high-level filter then updates its | 200 | a new context. The high-level filter then replaces its |
| 196 | internal context, and returns the processed chunk of data to | 201 | internal context, and returns the processed chunk of data to |
| 197 | the user. Notice that we take advantage of Lua's lexical | 202 | the user. Notice that we take advantage of Lua's lexical |
| 198 | scoping to store the context in a closure between function | 203 | scoping to store the context in a closure between function |
| 199 | calls. | 204 | calls. |
| 200 | 205 | ||
| 201 | Concerning the low-level filter code, we must first accept | 206 | \subsection{The C part of the filter} |
| 207 | |||
| 208 | As for the low-level filter, we must first accept | ||
| 202 | that there is no perfect solution to the end-of-line marker | 209 | that there is no perfect solution to the end-of-line marker |
| 203 | normalization problem. The difficulty comes from an | 210 | normalization problem. The difficulty comes from an |
| 204 | inherent ambiguity in the definition of empty lines within | 211 | inherent ambiguity in the definition of empty lines within |
| @@ -208,39 +215,39 @@ mixed input. It also does a reasonable job with empty lines | |||
| 208 | and serves as a good example of how to implement a low-level | 215 | and serves as a good example of how to implement a low-level |
| 209 | filter. | 216 | filter. |
| 210 | 217 | ||
| 211 | The idea is to consider both CR and~LF as end-of-line | 218 | The idea is to consider both \CR\ and~\LF\ as end-of-line |
| 212 | \emph{candidates}. We issue a single break if any candidate | 219 | \emph{candidates}. We issue a single break if any candidate |
| 213 | is seen alone, or followed by a different candidate. In | 220 | is seen alone, or if it is followed by a different |
| 214 | other words, CR~CR~and LF~LF each issue two end-of-line | 221 | candidate. In other words, \CR~\CR~and \LF~\LF\ each issue |
| 215 | markers, whereas CR~LF~and LF~CR issue only one marker each. | 222 | two end-of-line markers, whereas \CR~\LF~and \LF~\CR\ issue |
| 216 | This method correctly handles the Unix, DOS/MIME, VMS, and Mac | 223 | only one marker each. It is easy to see that this method |
| 217 | OS conventions. | 224 | correctly handles the most common end-of-line conventions. |
| 218 | 225 | ||
| 219 | \subsection{The C part of the filter} | 226 | With this in mind, we divide the low-level filter into two |
| 220 | 227 | simple functions. The inner function~\texttt{pushchar} performs the | |
| 221 | Our low-level filter is divided into two simple functions. | 228 | normalization itself. It takes each input character in turn, |
| 222 | The inner function performs the normalization itself. It takes | 229 | deciding what to output and how to modify the context. The |
| 223 | each input character in turn, deciding what to output and | 230 | context tells if the last processed character was an |
| 224 | how to modify the context. The context tells if the last | 231 | end-of-line candidate, and if so, which candidate it was. |
| 225 | processed character was an end-of-line candidate, and if so, | 232 | For efficiency, we use Lua's auxiliary library's buffer |
| 226 | which candidate it was. For efficiency, it uses | 233 | interface: |
| 227 | Lua's auxiliary library's buffer interface: | ||
| 228 | \begin{quote} | 234 | \begin{quote} |
| 229 | \begin{C} | 235 | \begin{C} |
| 230 | @stick# | 236 | @stick# |
| 231 | @#define candidate(c) (c == CR || c == LF) | 237 | @#define candidate(c) (c == CR || c == LF) |
| 232 | static int process(int c, int last, const char *marker, | 238 | static int pushchar(int c, int last, const char *marker, |
| 233 | luaL_Buffer *buffer) { | 239 | luaL_Buffer *buffer) { |
| 234 | if (candidate(c)) { | 240 | if (candidate(c)) { |
| 235 | if (candidate(last)) { | 241 | if (candidate(last)) { |
| 236 | if (c == last) luaL_addstring(buffer, marker); | 242 | if (c == last) |
| 243 | luaL_addstring(buffer, marker); | ||
| 237 | return 0; | 244 | return 0; |
| 238 | } else { | 245 | } else { |
| 239 | luaL_addstring(buffer, marker); | 246 | luaL_addstring(buffer, marker); |
| 240 | return c; | 247 | return c; |
| 241 | } | 248 | } |
| 242 | } else { | 249 | } else { |
| 243 | luaL_putchar(buffer, c); | 250 | luaL_pushchar(buffer, c); |
| 244 | return 0; | 251 | return 0; |
| 245 | } | 252 | } |
| 246 | } | 253 | } |
| @@ -248,15 +255,20 @@ static int process(int c, int last, const char *marker, | |||
| 248 | \end{C} | 255 | \end{C} |
| 249 | \end{quote} | 256 | \end{quote} |
| 250 | 257 | ||
| 251 | The outer function simply interfaces with Lua. It receives the | 258 | The outer function~\texttt{eol} simply interfaces with Lua. |
| 252 | context and input chunk (as well as an optional | 259 | It receives the context and input chunk (as well as an |
| 253 | custom end-of-line marker), and returns the transformed | 260 | optional custom end-of-line marker), and returns the |
| 254 | output chunk and the new context: | 261 | transformed output chunk and the new context. |
| 262 | Notice that if the input chunk is \nil, the operation | ||
| 263 | is considered to be finished. In that case, the loop will | ||
| 264 | not execute a single time and the context is reset to the | ||
| 265 | initial state. This allows the filter to be reused many | ||
| 266 | times: | ||
| 255 | \begin{quote} | 267 | \begin{quote} |
| 256 | \begin{C} | 268 | \begin{C} |
| 257 | @stick# | 269 | @stick# |
| 258 | static int eol(lua_State *L) { | 270 | static int eol(lua_State *L) { |
| 259 | int ctx = luaL_checkint(L, 1); | 271 | int context = luaL_checkint(L, 1); |
| 260 | size_t isize = 0; | 272 | size_t isize = 0; |
| 261 | const char *input = luaL_optlstring(L, 2, NULL, &isize); | 273 | const char *input = luaL_optlstring(L, 2, NULL, &isize); |
| 262 | const char *last = input + isize; | 274 | const char *last = input + isize; |
| @@ -269,24 +281,18 @@ static int eol(lua_State *L) { | |||
| 269 | return 2; | 281 | return 2; |
| 270 | } | 282 | } |
| 271 | while (input < last) | 283 | while (input < last) |
| 272 | ctx = process(*input++, ctx, marker, &buffer); | 284 | context = pushchar(*input++, context, marker, &buffer); |
| 273 | luaL_pushresult(&buffer); | 285 | luaL_pushresult(&buffer); |
| 274 | lua_pushnumber(L, ctx); | 286 | lua_pushnumber(L, context); |
| 275 | return 2; | 287 | return 2; |
| 276 | } | 288 | } |
| 277 | % | 289 | % |
| 278 | \end{C} | 290 | \end{C} |
| 279 | \end{quote} | 291 | \end{quote} |
| 280 | 292 | ||
| 281 | Notice that if the input chunk is \texttt{nil}, the operation | ||
| 282 | is considered to be finished. In that case, the loop will | ||
| 283 | not execute a single time and the context is reset to the | ||
| 284 | initial state. This allows the filter to be reused many | ||
| 285 | times. | ||
| 286 | |||
| 287 | When designing your own filters, the challenging part is to | 293 | When designing your own filters, the challenging part is to |
| 288 | decide what will be in the context. For line breaking, for | 294 | decide what will be in the context. For line breaking, for |
| 289 | instance, it could be the number of bytes left in the | 295 | instance, it could be the number of bytes that still fit in the |
| 290 | current line. For Base64 encoding, it could be a string | 296 | current line. For Base64 encoding, it could be a string |
| 291 | with the bytes that remain after the division of the input | 297 | with the bytes that remain after the division of the input |
| 292 | into 3-byte atoms. The MIME module in the \texttt{LuaSocket} | 298 | into 3-byte atoms. The MIME module in the \texttt{LuaSocket} |
| @@ -294,19 +300,22 @@ distribution has many other examples. | |||
| 294 | 300 | ||
| 295 | \section{Filter chains} | 301 | \section{Filter chains} |
| 296 | 302 | ||
| 297 | Chains add a lot to the power of filters. For example, | 303 | Chains greatly increase the power of filters. For example, |
| 298 | according to the standard for Quoted-Printable encoding, | 304 | according to the standard for Quoted-Printable encoding, |
| 299 | text must be normalized to a canonic end-of-line marker | 305 | text should be normalized to a canonic end-of-line marker |
| 300 | prior to encoding. To help specifying complex | 306 | prior to encoding. After encoding, the resulting text must |
| 301 | transformations like this, we define a chain factory that | 307 | be broken into lines of no more than 76 characters, with the |
| 302 | creates a composite filter from one or more filters. A | 308 | use of soft line breaks (a line terminated by the \texttt{=} |
| 303 | chained filter passes data through all its components, and | 309 | sign). To help specifying complex transformations like |
| 304 | can be used wherever a primitive filter is accepted. | 310 | this, we define a chain factory that creates a composite |
| 311 | filter from one or more filters. A chained filter passes | ||
| 312 | data through all its components, and can be used wherever a | ||
| 313 | primitive filter is accepted. | ||
| 305 | 314 | ||
| 306 | The chaining factory is very simple. The auxiliary | 315 | The chaining factory is very simple. The auxiliary |
| 307 | function~\texttt{chainpair} chains two filters together, | 316 | function~\texttt{chainpair} chains two filters together, |
| 308 | taking special care if the chunk is the last. This is | 317 | taking special care if the chunk is the last. This is |
| 309 | because the final \texttt{nil} chunk notification has to be | 318 | because the final \nil\ chunk notification has to be |
| 310 | pushed through both filters in turn: | 319 | pushed through both filters in turn: |
| 311 | \begin{quote} | 320 | \begin{quote} |
| 312 | \begin{lua} | 321 | \begin{lua} |
| @@ -322,9 +331,9 @@ end | |||
| 322 | 331 | ||
| 323 | @stick# | 332 | @stick# |
| 324 | function filter.chain(...) | 333 | function filter.chain(...) |
| 325 | local f = arg[1] | 334 | local f = select(1, ...) |
| 326 | for i = 2, @#arg do | 335 | for i = 2, select('@#', ...) do |
| 327 | f = chainpair(f, arg[i]) | 336 | f = chainpair(f, select(i, ...)) |
| 328 | end | 337 | end |
| 329 | return f | 338 | return f |
| 330 | end | 339 | end |
| @@ -337,11 +346,11 @@ define the Quoted-Printable conversion as such: | |||
| 337 | \begin{quote} | 346 | \begin{quote} |
| 338 | \begin{lua} | 347 | \begin{lua} |
| 339 | @stick# | 348 | @stick# |
| 340 | local qp = filter.chain(normalize("\r\n"), | 349 | local qp = filter.chain(normalize(CRLF), encode("quoted-printable"), |
| 341 | encode("quoted-printable")) | 350 | wrap("quoted-printable")) |
| 342 | local in = source.chain(source.file(io.stdin), qp) | 351 | local input = source.chain(source.file(io.stdin), qp) |
| 343 | local out = sink.file(io.stdout) | 352 | local output = sink.file(io.stdout) |
| 344 | pump.all(in, out) | 353 | pump.all(input, output) |
| 345 | % | 354 | % |
| 346 | \end{lua} | 355 | \end{lua} |
| 347 | \end{quote} | 356 | \end{quote} |
| @@ -360,14 +369,14 @@ gives a final destination to the data. | |||
| 360 | \subsection{Sources} | 369 | \subsection{Sources} |
| 361 | 370 | ||
| 362 | A source returns the next chunk of data each time it is | 371 | A source returns the next chunk of data each time it is |
| 363 | invoked. When there is no more data, it simply returns | 372 | invoked. When there is no more data, it simply returns~\nil. |
| 364 | \texttt{nil}. In the event of an error, the source can inform the | 373 | In the event of an error, the source can inform the |
| 365 | caller by returning \texttt{nil} followed by an error message. | 374 | caller by returning \nil\ followed by the error message. |
| 366 | 375 | ||
| 367 | Below are two simple source factories. The \texttt{empty} source | 376 | Below are two simple source factories. The \texttt{empty} source |
| 368 | returns no data, possibly returning an associated error | 377 | returns no data, possibly returning an associated error |
| 369 | message. The \texttt{file} source works harder, and | 378 | message. The \texttt{file} source yields the contents of a file |
| 370 | yields the contents of a file in a chunk by chunk fashion: | 379 | in a chunk by chunk fashion: |
| 371 | \begin{quote} | 380 | \begin{quote} |
| 372 | \begin{lua} | 381 | \begin{lua} |
| 373 | @stick# | 382 | @stick# |
| @@ -398,7 +407,7 @@ A filtered source passes its data through the | |||
| 398 | associated filter before returning it to the caller. | 407 | associated filter before returning it to the caller. |
| 399 | Filtered sources are useful when working with | 408 | Filtered sources are useful when working with |
| 400 | functions that get their input data from a source (such as | 409 | functions that get their input data from a source (such as |
| 401 | the pump in our first example). By chaining a source with one or | 410 | the pumps in our examples). By chaining a source with one or |
| 402 | more filters, the function can be transparently provided | 411 | more filters, the function can be transparently provided |
| 403 | with filtered data, with no need to change its interface. | 412 | with filtered data, with no need to change its interface. |
| 404 | Here is a factory that does the job: | 413 | Here is a factory that does the job: |
| @@ -406,14 +415,18 @@ Here is a factory that does the job: | |||
| 406 | \begin{lua} | 415 | \begin{lua} |
| 407 | @stick# | 416 | @stick# |
| 408 | function source.chain(src, f) | 417 | function source.chain(src, f) |
| 409 | return source.simplify(function() | 418 | return function() |
| 410 | if not src then return nil end | 419 | if not src then |
| 420 | return nil | ||
| 421 | end | ||
| 411 | local chunk, err = src() | 422 | local chunk, err = src() |
| 412 | if not chunk then | 423 | if not chunk then |
| 413 | src = nil | 424 | src = nil |
| 414 | return f(nil) | 425 | return f(nil) |
| 415 | else return f(chunk) end | 426 | else |
| 416 | end) | 427 | return f(chunk) |
| 428 | end | ||
| 429 | end | ||
| 417 | end | 430 | end |
| 418 | % | 431 | % |
| 419 | \end{lua} | 432 | \end{lua} |
| @@ -421,20 +434,20 @@ end | |||
| 421 | 434 | ||
| 422 | \subsection{Sinks} | 435 | \subsection{Sinks} |
| 423 | 436 | ||
| 424 | Just as we defined an interface a data source, | 437 | Just as we defined an interface for source of data, |
| 425 | we can also define an interface for a data destination. | 438 | we can also define an interface for a data destination. |
| 426 | We call any function respecting this | 439 | We call any function respecting this |
| 427 | interface a \emph{sink}. In our first example, we used a | 440 | interface a \emph{sink}. In our first example, we used a |
| 428 | file sink connected to the standard output. | 441 | file sink connected to the standard output. |
| 429 | 442 | ||
| 430 | Sinks receive consecutive chunks of data, until the end of | 443 | Sinks receive consecutive chunks of data, until the end of |
| 431 | data is signaled by a \texttt{nil} chunk. A sink can be | 444 | data is signaled by a \nil\ input chunk. A sink can be |
| 432 | notified of an error with an optional extra argument that | 445 | notified of an error with an optional extra argument that |
| 433 | contains the error message, following a \texttt{nil} chunk. | 446 | contains the error message, following a \nil\ chunk. |
| 434 | If a sink detects an error itself, and | 447 | If a sink detects an error itself, and |
| 435 | wishes not to be called again, it can return \texttt{nil}, | 448 | wishes not to be called again, it can return \nil, |
| 436 | followed by an error message. A return value that | 449 | followed by an error message. A return value that |
| 437 | is not \texttt{nil} means the source will accept more data. | 450 | is not \nil\ means the sink will accept more data. |
| 438 | 451 | ||
| 439 | Below are two useful sink factories. | 452 | Below are two useful sink factories. |
| 440 | The table factory creates a sink that stores | 453 | The table factory creates a sink that stores |
| @@ -469,7 +482,7 @@ end | |||
| 469 | 482 | ||
| 470 | Naturally, filtered sinks are just as useful as filtered | 483 | Naturally, filtered sinks are just as useful as filtered |
| 471 | sources. A filtered sink passes each chunk it receives | 484 | sources. A filtered sink passes each chunk it receives |
| 472 | through the associated filter before handing it to the | 485 | through the associated filter before handing it down to the |
| 473 | original sink. In the following example, we use a source | 486 | original sink. In the following example, we use a source |
| 474 | that reads from the standard input. The input chunks are | 487 | that reads from the standard input. The input chunks are |
| 475 | sent to a table sink, which has been coupled with a | 488 | sent to a table sink, which has been coupled with a |
| @@ -479,10 +492,10 @@ standard out: | |||
| 479 | \begin{quote} | 492 | \begin{quote} |
| 480 | \begin{lua} | 493 | \begin{lua} |
| 481 | @stick# | 494 | @stick# |
| 482 | local in = source.file(io.stdin) | 495 | local input = source.file(io.stdin) |
| 483 | local out, t = sink.table() | 496 | local output, t = sink.table() |
| 484 | out = sink.chain(normalize("\r\n"), out) | 497 | output = sink.chain(normalize(CRLF), output) |
| 485 | pump.all(in, out) | 498 | pump.all(input, output) |
| 486 | io.write(table.concat(t)) | 499 | io.write(table.concat(t)) |
| 487 | % | 500 | % |
| 488 | \end{lua} | 501 | \end{lua} |
| @@ -490,11 +503,11 @@ io.write(table.concat(t)) | |||
| 490 | 503 | ||
| 491 | \subsection{Pumps} | 504 | \subsection{Pumps} |
| 492 | 505 | ||
| 493 | Adrian Sietsma noticed that, although not on purpose, our | 506 | Although not on purpose, our interface for sources is |
| 494 | interface for sources is compatible with Lua iterators. | 507 | compatible with Lua iterators. That is, a source can be |
| 495 | That is, a source can be neatly used in conjunction | 508 | neatly used in conjunction with \texttt{for} loops. Using |
| 496 | with \texttt{for} loops. Using our file | 509 | our file source as an iterator, we can write the following |
| 497 | source as an iterator, we can write the following code: | 510 | code: |
| 498 | \begin{quote} | 511 | \begin{quote} |
| 499 | \begin{lua} | 512 | \begin{lua} |
| 500 | @stick# | 513 | @stick# |
| @@ -539,20 +552,22 @@ end | |||
| 539 | The \texttt{pump.step} function moves one chunk of data from | 552 | The \texttt{pump.step} function moves one chunk of data from |
| 540 | the source to the sink. The \texttt{pump.all} function takes | 553 | the source to the sink. The \texttt{pump.all} function takes |
| 541 | an optional \texttt{step} function and uses it to pump all the | 554 | an optional \texttt{step} function and uses it to pump all the |
| 542 | data from the source to the sink. We can now use everything | 555 | data from the source to the sink. |
| 543 | we have to write a program that reads a binary file from | 556 | Here is an example that uses the Base64 and the |
| 557 | line wrapping filters from the \texttt{LuaSocket} | ||
| 558 | distribution. The program reads a binary file from | ||
| 544 | disk and stores it in another file, after encoding it to the | 559 | disk and stores it in another file, after encoding it to the |
| 545 | Base64 transfer content encoding: | 560 | Base64 transfer content encoding: |
| 546 | \begin{quote} | 561 | \begin{quote} |
| 547 | \begin{lua} | 562 | \begin{lua} |
| 548 | @stick# | 563 | @stick# |
| 549 | local in = source.chain( | 564 | local input = source.chain( |
| 550 | source.file(io.open("input.bin", "rb")), | 565 | source.file(io.open("input.bin", "rb")), |
| 551 | encode("base64")) | 566 | encode("base64")) |
| 552 | local out = sink.chain( | 567 | local output = sink.chain( |
| 553 | wrap(76), | 568 | wrap(76), |
| 554 | sink.file(io.open("output.b64", "w"))) | 569 | sink.file(io.open("output.b64", "w"))) |
| 555 | pump.all(in, out) | 570 | pump.all(input, output) |
| 556 | % | 571 | % |
| 557 | \end{lua} | 572 | \end{lua} |
| 558 | \end{quote} | 573 | \end{quote} |
| @@ -561,19 +576,17 @@ The way we split the filters here is not intuitive, on | |||
| 561 | purpose. Alternatively, we could have chained the Base64 | 576 | purpose. Alternatively, we could have chained the Base64 |
| 562 | encode filter and the line-wrap filter together, and then | 577 | encode filter and the line-wrap filter together, and then |
| 563 | chain the resulting filter with either the file source or | 578 | chain the resulting filter with either the file source or |
| 564 | the file sink. It doesn't really matter. The Base64 and the | 579 | the file sink. It doesn't really matter. |
| 565 | line wrapping filters are part of the \texttt{LuaSocket} | ||
| 566 | distribution. | ||
| 567 | 580 | ||
| 568 | \section{Exploding filters} | 581 | \section{Exploding filters} |
| 569 | 582 | ||
| 570 | Our current filter interface has one flagrant shortcoming. | 583 | Our current filter interface has one serious shortcoming. |
| 571 | When David Burgess was writing his \texttt{gzip} filter, he | 584 | Consider for example a \texttt{gzip} decompression filter. |
| 572 | noticed that a decompression filter can explode a small | 585 | During decompression, a small input chunk can be exploded |
| 573 | input chunk into a huge amount of data. To address this | 586 | into a huge amount of data. To address this problem, we |
| 574 | problem, we decided to change the filter interface and allow | 587 | decided to change the filter interface and allow exploding |
| 575 | exploding filters to return large quantities of output data | 588 | filters to return large quantities of output data in a chunk |
| 576 | in a chunk by chunk manner. | 589 | by chunk manner. |
| 577 | 590 | ||
| 578 | More specifically, after passing each chunk of input to | 591 | More specifically, after passing each chunk of input to |
| 579 | a filter, and collecting the first chunk of output, the | 592 | a filter, and collecting the first chunk of output, the |
| @@ -582,11 +595,11 @@ filtered data is left. Within these secondary calls, the | |||
| 582 | caller passes an empty string to the filter. The filter | 595 | caller passes an empty string to the filter. The filter |
| 583 | responds with an empty string when it is ready for the next | 596 | responds with an empty string when it is ready for the next |
| 584 | input chunk. In the end, after the user passes a | 597 | input chunk. In the end, after the user passes a |
| 585 | \texttt{nil} chunk notifying the filter that there is no | 598 | \nil\ chunk notifying the filter that there is no |
| 586 | more input data, the filter might still have to produce too | 599 | more input data, the filter might still have to produce too |
| 587 | much output data to return in a single chunk. The user has | 600 | much output data to return in a single chunk. The user has |
| 588 | to loop again, now passing \texttt{nil} to the filter each time, | 601 | to loop again, now passing \nil\ to the filter each time, |
| 589 | until the filter itself returns \texttt{nil} to notify the | 602 | until the filter itself returns \nil\ to notify the |
| 590 | user it is finally done. | 603 | user it is finally done. |
| 591 | 604 | ||
| 592 | Fortunately, it is very easy to modify a filter to respect | 605 | Fortunately, it is very easy to modify a filter to respect |
| @@ -599,13 +612,13 @@ Interestingly, the modifications do not have a measurable | |||
| 599 | negative impact in the performance of filters that do | 612 | negative impact in the performance of filters that do |
| 600 | not need the added flexibility. On the other hand, for a | 613 | not need the added flexibility. On the other hand, for a |
| 601 | small price in complexity, the changes make exploding | 614 | small price in complexity, the changes make exploding |
| 602 | filters practical. | 615 | filters practical. |
| 603 | 616 | ||
| 604 | \section{A complex example} | 617 | \section{A complex example} |
| 605 | 618 | ||
| 606 | The LTN12 module in the \texttt{LuaSocket} distribution | 619 | The LTN12 module in the \texttt{LuaSocket} distribution |
| 607 | implements the ideas we have described. The MIME | 620 | implements all the ideas we have described. The MIME |
| 608 | and SMTP modules are especially integrated with LTN12, | 621 | and SMTP modules are tightly integrated with LTN12, |
| 609 | and can be used to showcase the expressive power of filters, | 622 | and can be used to showcase the expressive power of filters, |
| 610 | sources, sinks, and pumps. Below is an example | 623 | sources, sinks, and pumps. Below is an example |
| 611 | of how a user would proceed to define and send a | 624 | of how a user would proceed to define and send a |
| @@ -622,9 +635,9 @@ local message = smtp.message{ | |||
| 622 | to = "Fulano <fulano@example.com>", | 635 | to = "Fulano <fulano@example.com>", |
| 623 | subject = "A message with an attachment"}, | 636 | subject = "A message with an attachment"}, |
| 624 | body = { | 637 | body = { |
| 625 | preamble = "Hope you can see the attachment\r\n", | 638 | preamble = "Hope you can see the attachment" .. CRLF, |
| 626 | [1] = { | 639 | [1] = { |
| 627 | body = "Here is our logo\r\n"}, | 640 | body = "Here is our logo" .. CRLF}, |
| 628 | [2] = { | 641 | [2] = { |
| 629 | headers = { | 642 | headers = { |
| 630 | ["content-type"] = 'image/png; name="luasocket.png"', | 643 | ["content-type"] = 'image/png; name="luasocket.png"', |
| @@ -665,6 +678,18 @@ abstraction for final data destinations. Filters define an | |||
| 665 | interface for data transformations. The chaining of | 678 | interface for data transformations. The chaining of |
| 666 | filters, sources and sinks provides an elegant way to create | 679 | filters, sources and sinks provides an elegant way to create |
| 667 | arbitrarily complex data transformations from simpler | 680 | arbitrarily complex data transformations from simpler |
| 668 | components. Pumps simply move the data through. | 681 | components. Pumps simply push the data through. |
| 682 | |||
| 683 | \section{Acknowledgements} | ||
| 684 | |||
| 685 | The concepts described in this text are the result of long | ||
| 686 | discussions with David Burgess. A version of this text has | ||
| 687 | been released on-line as the Lua Technical Note 012, hence | ||
| 688 | the name of the corresponding LuaSocket module, | ||
| 689 | \texttt{ltn12}. Wim Couwenberg contributed to the | ||
| 690 | implementation of the module, and Adrian Sietsma was the | ||
| 691 | first to notice the correspondence between sources and Lua | ||
| 692 | iterators. | ||
| 693 | |||
| 669 | 694 | ||
| 670 | \end{document} | 695 | \end{document} |
diff --git a/gem/luasocket.png b/gem/luasocket.png new file mode 100644 index 0000000..d24a954 --- /dev/null +++ b/gem/luasocket.png | |||
| Binary files differ | |||
diff --git a/gem/makefile b/gem/makefile index a4287c2..d2f0c93 100644 --- a/gem/makefile +++ b/gem/makefile | |||
| @@ -12,3 +12,12 @@ clean: | |||
| 12 | 12 | ||
| 13 | pdf: ltn012.pdf | 13 | pdf: ltn012.pdf |
| 14 | open ltn012.pdf | 14 | open ltn012.pdf |
| 15 | |||
| 16 | test: gem.so | ||
| 17 | |||
| 18 | |||
| 19 | gem.o: gem.c | ||
| 20 | gcc -c -o gem.o -Wall -ansi -W -O2 gem.c | ||
| 21 | |||
| 22 | gem.so: gem.o | ||
| 23 | export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc -bundle -undefined dynamic_lookup -o gem.so gem.o | ||
diff --git a/gem/t1.lua b/gem/t1.lua new file mode 100644 index 0000000..0c054c9 --- /dev/null +++ b/gem/t1.lua | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | source = {} | ||
| 2 | sink = {} | ||
| 3 | pump = {} | ||
| 4 | filter = {} | ||
| 5 | |||
| 6 | -- source.chain | ||
| 7 | dofile("ex6.lua") | ||
| 8 | |||
| 9 | -- source.file | ||
| 10 | dofile("ex5.lua") | ||
| 11 | |||
| 12 | -- normalize | ||
| 13 | require"gem" | ||
| 14 | eol = gem.eol | ||
| 15 | dofile("ex2.lua") | ||
| 16 | |||
| 17 | -- sink.file | ||
| 18 | require"ltn12" | ||
| 19 | sink.file = ltn12.sink.file | ||
| 20 | |||
| 21 | -- pump.all | ||
| 22 | dofile("ex10.lua") | ||
| 23 | |||
| 24 | -- run test | ||
| 25 | dofile("ex1.lua") | ||
diff --git a/gem/t1lf.txt b/gem/t1lf.txt new file mode 100644 index 0000000..8cddd1b --- /dev/null +++ b/gem/t1lf.txt | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | this is a test file | ||
| 2 | it should have been saved as lf eol | ||
| 3 | but t1.lua will convert it to crlf eol | ||
| 4 | otherwise it is broken! | ||
| 5 | |||
diff --git a/gem/t2.lua b/gem/t2.lua new file mode 100644 index 0000000..a81ed73 --- /dev/null +++ b/gem/t2.lua | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | source = {} | ||
| 2 | sink = {} | ||
| 3 | pump = {} | ||
| 4 | filter = {} | ||
| 5 | |||
| 6 | -- filter.chain | ||
| 7 | dofile("ex3.lua") | ||
| 8 | |||
| 9 | -- normalize | ||
| 10 | require"gem" | ||
| 11 | eol = gem.eol | ||
| 12 | dofile("ex2.lua") | ||
| 13 | |||
| 14 | -- encode | ||
| 15 | require"mime" | ||
| 16 | encode = mime.encode | ||
| 17 | |||
| 18 | -- wrap | ||
| 19 | wrap = mime.wrap | ||
| 20 | |||
| 21 | -- source.chain | ||
| 22 | dofile("ex6.lua") | ||
| 23 | |||
| 24 | -- source.file | ||
| 25 | dofile("ex5.lua") | ||
| 26 | |||
| 27 | -- sink.file | ||
| 28 | require"ltn12" | ||
| 29 | sink.file = ltn12.sink.file | ||
| 30 | |||
| 31 | -- pump.all | ||
| 32 | dofile("ex10.lua") | ||
| 33 | |||
| 34 | -- run test | ||
| 35 | CRLF = "\013\010" | ||
| 36 | dofile("ex4.lua") | ||
diff --git a/gem/t2.txt b/gem/t2.txt new file mode 100644 index 0000000..f484fe8 --- /dev/null +++ b/gem/t2.txt | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | esse é um texto com acentos | ||
| 2 | quoted-printable tem que quebrar linhas longas, com mais que 76 linhas de texto | ||
| 3 | fora que as quebras de linhas têm que ser normalizadas | ||
| 4 | vamos ver o que dá isso aqui | ||
diff --git a/gem/t2gt.qp b/gem/t2gt.qp new file mode 100644 index 0000000..355a845 --- /dev/null +++ b/gem/t2gt.qp | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | esse =E9 um texto com acentos | ||
| 2 | quoted-printable tem que quebrar linhas longas, com mais que 76 linhas de t= | ||
| 3 | exto | ||
| 4 | fora que as quebras de linhas t=EAm que ser normalizadas | ||
| 5 | vamos ver o que d=E1 isso aqui | ||
diff --git a/gem/t3.lua b/gem/t3.lua new file mode 100644 index 0000000..4bb98ba --- /dev/null +++ b/gem/t3.lua | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | source = {} | ||
| 2 | sink = {} | ||
| 3 | pump = {} | ||
| 4 | filter = {} | ||
| 5 | |||
| 6 | -- source.file | ||
| 7 | dofile("ex5.lua") | ||
| 8 | |||
| 9 | -- sink.table | ||
| 10 | dofile("ex7.lua") | ||
| 11 | |||
| 12 | -- sink.chain | ||
| 13 | require"ltn12" | ||
| 14 | sink.chain = ltn12.sink.chain | ||
| 15 | |||
| 16 | -- normalize | ||
| 17 | require"gem" | ||
| 18 | eol = gem.eol | ||
| 19 | dofile("ex2.lua") | ||
| 20 | |||
| 21 | -- pump.all | ||
| 22 | dofile("ex10.lua") | ||
| 23 | |||
| 24 | -- run test | ||
| 25 | dofile("ex8.lua") | ||
diff --git a/gem/t4.lua b/gem/t4.lua new file mode 100644 index 0000000..8b8071c --- /dev/null +++ b/gem/t4.lua | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | source = {} | ||
| 2 | sink = {} | ||
| 3 | pump = {} | ||
| 4 | filter = {} | ||
| 5 | |||
| 6 | -- source.file | ||
| 7 | dofile("ex5.lua") | ||
| 8 | |||
| 9 | -- run test | ||
| 10 | dofile("ex9.lua") | ||
diff --git a/gem/t5.lua b/gem/t5.lua new file mode 100644 index 0000000..7c569ea --- /dev/null +++ b/gem/t5.lua | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | source = {} | ||
| 2 | sink = {} | ||
| 3 | pump = {} | ||
| 4 | filter = {} | ||
| 5 | |||
| 6 | -- source.chain | ||
| 7 | dofile("ex6.lua") | ||
| 8 | |||
| 9 | -- source.file | ||
| 10 | dofile("ex5.lua") | ||
| 11 | |||
| 12 | -- encode | ||
| 13 | require"mime" | ||
| 14 | encode = mime.encode | ||
| 15 | |||
| 16 | -- sink.chain | ||
| 17 | require"ltn12" | ||
| 18 | sink.chain = ltn12.sink.chain | ||
| 19 | |||
| 20 | -- wrap | ||
| 21 | wrap = mime.wrap | ||
| 22 | |||
| 23 | -- sink.file | ||
| 24 | sink.file = ltn12.sink.file | ||
| 25 | |||
| 26 | -- pump.all | ||
| 27 | dofile("ex10.lua") | ||
| 28 | |||
| 29 | -- run test | ||
| 30 | dofile("ex11.lua") | ||
diff --git a/gem/test.lua b/gem/test.lua new file mode 100644 index 0000000..a937b9a --- /dev/null +++ b/gem/test.lua | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | function readfile(n) | ||
| 2 | local f = io.open(n, "rb") | ||
| 3 | local s = f:read("*a") | ||
| 4 | f:close() | ||
| 5 | return s | ||
| 6 | end | ||
| 7 | |||
| 8 | lf = readfile("t1lf.txt") | ||
| 9 | os.remove("t1crlf.txt") | ||
| 10 | os.execute("lua t1.lua < t1lf.txt > t1crlf.txt") | ||
| 11 | crlf = readfile("t1crlf.txt") | ||
| 12 | assert(crlf == string.gsub(lf, "\010", "\013\010"), "broken") | ||
| 13 | |||
| 14 | gt = readfile("t2gt.qp") | ||
| 15 | os.remove("t2.qp") | ||
| 16 | os.execute("lua t2.lua < t2.txt > t2.qp") | ||
| 17 | t2 = readfile("t2.qp") | ||
| 18 | assert(gt == t2, "broken") | ||
| 19 | |||
| 20 | os.remove("t1crlf.txt") | ||
| 21 | os.execute("lua t3.lua < t1lf.txt > t1crlf.txt") | ||
| 22 | crlf = readfile("t1crlf.txt") | ||
| 23 | assert(crlf == string.gsub(lf, "\010", "\013\010"), "broken") | ||
| 24 | |||
| 25 | t = readfile("test.lua") | ||
| 26 | os.execute("lua t4.lua < test.lua > t") | ||
| 27 | t2 = readfile("t") | ||
| 28 | assert(t == t2, "broken") | ||
| 29 | |||
| 30 | os.remove("output.b64") | ||
| 31 | gt = readfile("gt.b64") | ||
| 32 | os.execute("lua t5.lua") | ||
| 33 | t5 = readfile("output.b64") | ||
| 34 | assert(gt == t5, "failed") | ||
| 35 | |||
| 36 | print("1 2 5 6 10 passed") | ||
| 37 | print("2 3 4 5 6 10 passed") | ||
| 38 | print("2 5 6 7 8 10 passed") | ||
| 39 | print("5 9 passed") | ||
| 40 | print("5 6 10 11 passed") | ||
| 41 | |||
| 42 | os.remove("t") | ||
| 43 | os.remove("t2.qp") | ||
| 44 | os.remove("t1crlf.txt") | ||
| 45 | os.remove("t11.b64") | ||
| 46 | os.remove("output.b64") | ||
