summaryrefslogtreecommitdiff
path: root/tex/context/base/mkxl/libs-imp-foreign.mkxl
blob: 5c266358629f84d61b3fdea3cb835331f2b30e9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
% permitloadlib=true

%D \module
%D   [       file=libs-imp-foreign,
%D        version=2021.03.10,
%D          title=\CONTEXT\ Extra Modules,
%D       subtitle=Basic FFI,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

%D This module was added as a side track of a user wanting to run a library that is
%D not (has support) built in. In order to identify issues I wondered if we could
%D have an additional feature to the already present optional libraries (of course
%D with the usual loadlib protection. Keep in mind that loading libraries creates a
%D dependency and an api can change. And in a long term program like any \TEX\
%D program we don't want that (at least I don't want users to be forced to install
%D lots of additional source code and dependencies in order to compile \LUAMETATEX\
%D successfully.
%D
%D So, I looked around for alternatives to ffi and ran into the (stable since 5
%D years) alien module (by Fabio Mascarenhas) that uses the rather portable libffi
%D but it does have some dependencies. For callbacks you need to set some parameters
%D normally dealt with when configuring and compiling which does creates a
%D dependency. So, in the end I just took its keyword driven approach but wrapped it
%D in alternative code that more matches other modules and assumes \LUA\ 5.4.
%D
%D Additional features like arrays and structs will be implemented when we need them
%D using modern \LUA\ 5.4 features (string packing, toclose, etc). For now I
%D consider all this an experiment and will pick up the thread when I have an
%D example.
%D
%D So, how far do we go? I think as soon as a library becomes more complex, say with
%D multi|-|dimensional arrays one should just write a proper interface. So we limit
%D ourselves here. One problem with more complex datastructures is that it opens the
%D door to abuse thanks to uncontrolled memory access.

\registerctxluafile{libs-imp-foreign}{autosuffix}

\continueifinputfile{libs-imp-foreign.mkxl}

%D The difference in performance is not that significant because the time spent in
%D the called function is the bottleneck here.

\usemodule[article-basic] \setupbodyfont[8pt] \noheaderandfooterlines

\starttext

\startluacode

    local NC, BC, NR = context.NC, context.BC, context.NR

    function document.identify(kpse,set_program_name,find_file)
        context.starttabulate { "|l|lp|" }
            BC() context.type("kpse")             NC() context.typ(tostring(kpse))             NC() NR()
            BC() context.type("set_program_name") NC() context.typ(tostring(set_program_name)) NC() NR()
            BC() context.type("find_file")        NC() context.typ(tostring(find_file))        NC() NR()
        context.stoptabulate()
    end

    function document.lookup(find_file,filename,filetype,present,n)

        local c = os.clock()
        for i=1,n do
            if find_file(filename,filetype,present) then
                -- okay
            end
        end
        c = os.clock() - c

        context.starttabulate()
            BC() context("asked") NC() context.type(filename)                             NC() NR()
            BC() context("found") NC() context.type(find_file(filename,filetype,present)) NC() NR()
            if n > 0 then
                BC() context("times")   NC() context(n)            NC() NR()
                BC() context("seconds") NC() context(" %0.3f",c)   NC() NR()
                BC() context("lookup")  NC() context(" %0.6f",c/n) NC() NR()
            end
        context.stoptabulate()
    end
\stopluacode

\starttitle[title=kpse via foreign]

\startluacode

    local foreign = optional.loaded.foreign

    local kplib = (os.platform == "win64" and "kpathsea*w64")
               or (os.platform == "win32" and "kpathsea*w32")
               or "libkpathsea"

    local kpse  = foreign.load(kplib)

    local set_program_name = kpse:register {
        name      = "kpse_set_program_name",
        arguments = { "string", "string" }
    }

    local find_file = kpse:register {
        name      = "kpse_find_file",
        arguments = { "string", "int", "int" },
        result    = "string",
    }
    local path_expand = kpse:register {
        name      = "kpse_path_expand",
        arguments = { "string" },
        result    = "string",
    }
    local all_path_search = kpse:register {
        name      = "kpse_all_path_search",
        arguments = { "string", "string" },
        result    = "pointer",
        finalizer = function(p)
            return foreign.totable(p,"string") -- unknown n, so NULL terminated
        end
    }

 -- print(kpse)
 -- print(set_program_name)
 -- print(kpse:registered("kpse_set_program_name"))
 -- print(kpse:available("kpse_set_program_name"))
 -- print(kpse:available("set_program_name"))
 -- print(kpse:available("kpse_find_file"))

 -- inspect(kpse:registered ())
 -- inspect(foreign.types())
 -- inspect(foreign.abivalues())

    local set_program_name = kpse:registered("kpse_set_program_name")
    local find_file        = kpse:registered("kpse_find_file")

    set_program_name("luatex","luatex")

    document.lookup(find_file, "libs-imp-foreign.mkxl", 26, 0, 100000)
    document.lookup(find_file, "oeps.tex",              26, 0,  10000)
    document.lookup(find_file, "metafun.mp",            16, 0,   5000)
    document.lookup(find_file, "logo10.afm",             4, 0,   2500)

    document.identify(kpse, set_program_name, find_file)

 -- set_program_name("pdftex","pdftex")
 --
 -- local t = path_expand("$TEXINPUTS")
 -- local p = all_path_search(t,"oeps.tex")
 --
 -- inspect(t)
 -- inspect(p)

\stopluacode

\stoptitle

\registerctxluafile{libs-imp-kpse}{autosuffix}

\starttitle[title=kpse via optional / string]

\startluacode

    local kpse = optional.loaded.kpse

    local set_program_name = kpse.set_program_name
    local find_file        = kpse.find_file

    kpse.set_program_name("luatex")

    document.lookup(find_file, "libs-imp-foreign.mkxl", "tex", false, 100000)
    document.lookup(find_file, "oeps.tex",              "tex", false,  10000)
    document.lookup(find_file, "metafun.mp",            "mp",  false,   5000)
    document.lookup(find_file, "logo10.afm",            "afm", false,   2500)

    document.identify(kpse, set_program_name, find_file)

\stopluacode

\stoptitle

\starttitle[title=kpse via optional / number]

\startluacode

    local kpse = optional.loaded.kpse

    local set_program_name = kpse.set_program_name
    local find_file        = kpse.find_file

    kpse.set_program_name("luatex")

    document.lookup(find_file, "libs-imp-foreign.mkxl", 26, false, 100000)
    document.lookup(find_file, "oeps.tex",              26, false,  10000)
    document.lookup(find_file, "metafun.mp",            16, false,   5000)
    document.lookup(find_file, "logo10.afm",             4, false,   2500)

    document.identify(kpse, set_program_name, find_file)

\stopluacode

\stoptitle

\stoptext