libs-imp-foreign.mkxl /size: 7128 b    last modification: 2021-10-28 13:51
1% permitloadlib=true
2
3%D \module
4%D   [       file=libs-imp-foreign,
5%D        version=2021.03.10,
6%D          title=\CONTEXT\ Extra Modules,
7%D       subtitle=Basic FFI,
8%D         author=Hans Hagen,
9%D           date=\currentdate,
10%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
11%C
12%C This module is part of the \CONTEXT\ macro||package and is
13%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
14%C details.
15
16%D This module was added as a side track of a user wanting to run a library that is
17%D not (has support) built in. In order to identify issues I wondered if we could
18%D have an additional feature to the already present optional libraries (of course
19%D with the usual loadlib protection. Keep in mind that loading libraries creates a
20%D dependency and an api can change. And in a long term program like any \TEX\
21%D program we don't want that (at least I don't want users to be forced to install
22%D lots of additional source code and dependencies in order to compile \LUAMETATEX\
23%D successfully.
24%D
25%D So, I looked around for alternatives to ffi and ran into the (stable since 5
26%D years) alien module (by Fabio Mascarenhas) that uses the rather portable libffi
27%D but it does have some dependencies. For callbacks you need to set some parameters
28%D normally dealt with when configuring and compiling which does creates a
29%D dependency. So, in the end I just took its keyword driven approach but wrapped it
30%D in alternative code that more matches other modules and assumes \LUA\ 5.4.
31%D
32%D Additional features like arrays and structs will be implemented when we need them
33%D using modern \LUA\ 5.4 features (string packing, toclose, etc). For now I
34%D consider all this an experiment and will pick up the thread when I have an
35%D example.
36%D
37%D So, how far do we go? I think as soon as a library becomes more complex, say with
38%D multi|-|dimensional arrays one should just write a proper interface. So we limit
39%D ourselves here. One problem with more complex datastructures is that it opens the
40%D door to abuse thanks to uncontrolled memory access.
41
42\registerctxluafile{libs-imp-foreign}{autosuffix}
43
44\continueifinputfile{libs-imp-foreign.mkxl}
45
46%D The difference in performance is not that significant because the time spent in
47%D the called function is the bottleneck here.
48
49\usemodule[article-basic] \setupbodyfont[8pt] \noheaderandfooterlines
50
51\starttext
52
53\startluacode
54
55    local NC, BC, NR = context.NC, context.BC, context.NR
56
57    function document.identify(kpse,set_program_name,find_file)
58        context.starttabulate { "|l|lp|" }
59            BC() context.type("kpse")             NC() context.typ(tostring(kpse))             NC() NR()
60            BC() context.type("set_program_name") NC() context.typ(tostring(set_program_name)) NC() NR()
61            BC() context.type("find_file")        NC() context.typ(tostring(find_file))        NC() NR()
62        context.stoptabulate()
63    end
64
65    function document.lookup(find_file,filename,filetype,present,n)
66
67        local c = os.clock()
68        for i=1,n do
69            if find_file(filename,filetype,present) then
70                -- okay
71            end
72        end
73        c = os.clock() - c
74
75        context.starttabulate()
76            BC() context("asked") NC() context.type(filename)                             NC() NR()
77            BC() context("found") NC() context.type(find_file(filename,filetype,present)) NC() NR()
78            if n > 0 then
79                BC() context("times")   NC() context(n)            NC() NR()
80                BC() context("seconds") NC() context(" %0.3f",c)   NC() NR()
81                BC() context("lookup")  NC() context(" %0.6f",c/n) NC() NR()
82            end
83        context.stoptabulate()
84    end
85\stopluacode
86
87\starttitle[title=kpse via foreign]
88
89\startluacode
90
91    local foreign = optional.loaded.foreign
92
93    local kplib = (os.platform == "win64" and "kpathsea*w64")
94               or (os.platform == "win32" and "kpathsea*w32")
95               or "libkpathsea"
96
97    local kpse  = foreign.load(kplib)
98
99    local set_program_name = kpse:register {
100        name      = "kpse_set_program_name",
101        arguments = { "string", "string" }
102    }
103
104    local find_file = kpse:register {
105        name      = "kpse_find_file",
106        arguments = { "string", "int", "int" },
107        result    = "string",
108    }
109    local path_expand = kpse:register {
110        name      = "kpse_path_expand",
111        arguments = { "string" },
112        result    = "string",
113    }
114    local all_path_search = kpse:register {
115        name      = "kpse_all_path_search",
116        arguments = { "string", "string" },
117        result    = "pointer",
118        finalizer = function(p)
119            return foreign.totable(p,"string") -- unknown n, so NULL terminated
120        end
121    }
122
123 -- print(kpse)
124 -- print(set_program_name)
125 -- print(kpse:registered("kpse_set_program_name"))
126 -- print(kpse:available("kpse_set_program_name"))
127 -- print(kpse:available("set_program_name"))
128 -- print(kpse:available("kpse_find_file"))
129
130 -- inspect(kpse:registered ())
131 -- inspect(foreign.types())
132 -- inspect(foreign.abivalues())
133
134    local set_program_name = kpse:registered("kpse_set_program_name")
135    local find_file        = kpse:registered("kpse_find_file")
136
137    set_program_name("luatex","luatex")
138
139    document.lookup(find_file, "libs-imp-foreign.mkxl", 26, 0, 100000)
140    document.lookup(find_file, "oeps.tex",              26, 0,  10000)
141    document.lookup(find_file, "metafun.mp",            16, 0,   5000)
142    document.lookup(find_file, "logo10.afm",             4, 0,   2500)
143
144    document.identify(kpse, set_program_name, find_file)
145
146 -- set_program_name("pdftex","pdftex")
147 --
148 -- local t = path_expand("$TEXINPUTS")
149 -- local p = all_path_search(t,"oeps.tex")
150 --
151 -- inspect(t)
152 -- inspect(p)
153
154\stopluacode
155
156\stoptitle
157
158\registerctxluafile{libs-imp-kpse}{autosuffix}
159
160\starttitle[title=kpse via optional / string]
161
162\startluacode
163
164    local kpse = optional.loaded.kpse
165
166    local set_program_name = kpse.set_program_name
167    local find_file        = kpse.find_file
168
169    kpse.set_program_name("luatex")
170
171    document.lookup(find_file, "libs-imp-foreign.mkxl", "tex", false, 100000)
172    document.lookup(find_file, "oeps.tex",              "tex", false,  10000)
173    document.lookup(find_file, "metafun.mp",            "mp",  false,   5000)
174    document.lookup(find_file, "logo10.afm",            "afm", false,   2500)
175
176    document.identify(kpse, set_program_name, find_file)
177
178\stopluacode
179
180\stoptitle
181
182\starttitle[title=kpse via optional / number]
183
184\startluacode
185
186    local kpse = optional.loaded.kpse
187
188    local set_program_name = kpse.set_program_name
189    local find_file        = kpse.find_file
190
191    kpse.set_program_name("luatex")
192
193    document.lookup(find_file, "libs-imp-foreign.mkxl", 26, false, 100000)
194    document.lookup(find_file, "oeps.tex",              26, false,  10000)
195    document.lookup(find_file, "metafun.mp",            16, false,   5000)
196    document.lookup(find_file, "logo10.afm",             4, false,   2500)
197
198    document.identify(kpse, set_program_name, find_file)
199
200\stopluacode
201
202\stoptitle
203
204\stoptext
205