luat-cbk.lua /size: 12 Kb    last modification: 2021-10-28 13:50
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
luat-cbk
'
]
=
{
2
version
=
1
.
001
,
3
comment
=
"
companion to luat-lib.mkiv
"
,
4
author
=
"
Hans Hagen, PRAGMA-ADE, Hasselt NL
"
,
5
copyright
=
"
PRAGMA ADE / ConTeXt Development Team
"
,
6
license
=
"
see context related readme files
"
7
}
8 9
local
insert
,
remove
,
concat
=
table
.
insert
,
table
.
remove
,
table
.
concat
10
local
find
,
format
=
string
.
find
,
string
.
format
11
local
collectgarbage
,
type
,
next
=
collectgarbage
,
type
,
next
12
local
round
=
math
.
round
13
local
sortedhash
,
sortedkeys
,
tohash
=
table
.
sortedhash
,
table
.
sortedkeys
,
table
.
tohash
14 15
--[[ldx-- 16<p>Callbacks are the real asset of <l n='luatex'/>. They permit you to hook 17your own code into the <l n='tex'/> engine. Here we implement a few handy 18auxiliary functions.</p> 19--ldx]]
--
20 21
callbacks
=
callbacks
or
{
}
22
local
callbacks
=
callbacks
23 24
--[[ldx-- 25<p>When you (temporarily) want to install a callback function, and after a 26while wants to revert to the original one, you can use the following two 27functions. This only works for non-frozen ones.</p> 28--ldx]]
--
29 30
local
trace_callbacks
=
false
trackers
.
register
(
"
system.callbacks
"
,
function
(
v
)
trace_callbacks
=
v
end
)
31
local
trace_calls
=
false
-- only used when analyzing performance and initializations
32
local
trace_checking
=
false
trackers
.
register
(
"
memory.checking
"
,
function
(
v
)
trace_checking
=
v
end
)
33 34
local
report_system
=
logs
.
reporter
(
"
system
"
)
35
local
report_callbacks
=
logs
.
reporter
(
"
system
"
,
"
callbacks
"
)
36
local
report_memory
=
logs
.
reporter
(
"
system
"
,
"
memory
"
)
37 38
local
register_callback
=
callback
.
register
39
local
find_callback
=
callback
.
find
40
local
list_callbacks
=
callback
.
list
41
local
register_usercall
=
false
42
local
original_register
=
register_callback
43 44
local
frozen
=
{
}
45
local
stack
=
{
}
46
local
list
=
callbacks
.
list
47
local
permit_overloads
=
false
48
local
block_overloads
=
false
49 50
--[[ldx-- 51<p>By now most callbacks are frozen and most provide a way to plug in your own code. For instance 52all node list handlers provide before/after namespaces and the file handling code can be extended 53by adding schemes and if needed I can add more hooks. So there is no real need to overload a core 54callback function. It might be ok for quick and dirty testing but anyway you're on your own if 55you permanently overload callback functions.</p> 56--ldx]]
--
57 58
-- This might become a configuration file only option when it gets abused too much.
59 60
directives
.
register
(
"
system.callbacks.permitoverloads
"
,
function
(
v
)
61
if
block_overloads
or
permit_overloads
then
62
-- once bad news, always bad news
63
elseif
v
then
64
permit_overloads
=
{
}
65
report_system
(
)
66
report_system
(
"
The callback system has been brought in an unprotected state. As a result of directly
"
)
67
report_system
(
"
setting of callbacks subsystems of ConTeXt can stop working. There is no support for
"
)
68
report_system
(
"
bugs resulting from this state. It's better to use the official extension mechanisms.
"
)
69
report_system
(
)
70
end
71
end
)
72 73
sandbox
.
initializer
{
74
category
=
"
functions
"
,
75
action
=
function
(
)
76
block_overloads
=
true
77
end
78
}
79 80
if
not
list
then
-- otherwise counters get reset
81 82
list
=
utilities
.
storage
.
allocate
(
list_callbacks
(
)
)
83 84
local
supported
=
{
}
85 86
for
k
in
next
,
list
do
87
list
[
k
]
=
0
88
supported
[
k
]
=
true
89
end
90 91
callbacks
.
list
=
list
92
callbacks
.
supported
=
supported
93 94
end
95 96
local
delayed
=
tohash
{
97
"
buildpage_filter
"
,
98
}
99 100
if
trace_calls
then
101 102
local
functions
=
{
}
103 104
register_callback
=
function
(
name
,
func
)
105
if
type
(
func
)
=
=
"
function
"
then
106
if
functions
[
name
]
then
107
functions
[
name
]
=
func
108
return
find_callback
(
name
)
109
else
110
functions
[
name
]
=
func
111
local
cnuf
=
function
(
...
)
112
list
[
name
]
=
list
[
name
]
+
1
113
return
functions
[
name
]
(
...
)
114
end
115
return
original_register
(
name
,
cnuf
)
116
end
117
else
118
return
original_register
(
name
,
func
)
119
end
120
end
121 122
end
123 124
-- temporary, not public:
125 126
callbacks
.
functions
=
{
}
127 128
-- till here
129 130
local
reported
=
{
}
131 132
local
function
register_usercall
(
what
,
name
,
func
)
133
if
list
[
name
]
then
134
if
trace_callbacks
or
not
reported
[
name
]
then
135
report_system
(
)
136
report_system
(
"
disabling core code by %s user function into callback '%s' (reported only once)
"
,
what
,
name
)
137
report_system
(
)
138
reported
[
name
]
=
true
139
end
140
permit_overloads
[
name
]
=
true
141
return
original_register
(
name
,
function
(
...
)
142
if
trace_callbacks
then
143
report_callbacks
(
"
calling user function from '%s'
"
,
name
)
144
end
145
return
func
(
...
)
146
end
)
147
else
148
report_callbacks
(
"
not %s function into invalid callback '%s'
"
,
name
)
149
return
nil
,
format
(
"
unknown callback '%s'
"
,
name
)
150
end
151
end
152 153
local
function
frozen_callback
(
name
)
154
report_callbacks
(
"
not %s frozen %a
"
,
"
registering
"
,
name
)
155
return
nil
,
format
(
"
callback '%s' is frozen
"
,
name
)
-- no formatter yet
156
end
157 158
local
function
state
(
name
)
159
local
f
=
find_callback
(
name
)
160
if
f
=
=
false
then
161
return
"
disabled
"
162
elseif
f
then
163
return
"
enabled
"
164
else
165
return
"
undefined
"
166
end
167
end
168 169
function
callbacks
.
known
(
name
)
170
return
list
[
name
]
171
end
172 173
function
callbacks
.
report
(
)
174
for
name
,
_
in
sortedhash
(
list
)
do
175
local
str
=
frozen
[
name
]
176
if
str
then
177
report_callbacks
(
"
%s: %s -> %s
"
,
state
(
name
)
,
name
,
str
)
178
else
179
report_callbacks
(
"
%s: %s
"
,
state
(
name
)
,
name
)
180
end
181
end
182
end
183 184
function
callbacks
.
freeze
(
name
,
freeze
)
185
if
not
permit_overloads
then
186
freeze
=
type
(
freeze
)
=
=
"
string
"
and
freeze
187
if
find
(
name
,
"
*
"
,
1
,
true
)
then
188
local
pattern
=
name
189
for
name
,
_
in
next
,
list
do
190
if
find
(
name
,
pattern
)
then
191
frozen
[
name
]
=
freeze
or
frozen
[
name
]
or
"
frozen
"
192
end
193
end
194
else
195
frozen
[
name
]
=
freeze
or
frozen
[
name
]
or
"
frozen
"
196
end
197
end
198
end
199 200
function
callbacks
.
register
(
name
,
func
,
freeze
)
201
if
frozen
[
name
]
then
202
if
permit_overloads
then
203
return
register_usercall
(
"
registering
"
,
name
,
func
)
204
else
205
return
frozen_callback
(
name
)
206
end
207
elseif
freeze
then
208
frozen
[
name
]
=
type
(
freeze
)
=
=
"
string
"
and
freeze
or
"
registered
"
209
end
210
if
delayed
[
name
]
and
environment
.
initex
then
211
return
nil
212
end
213
return
register_callback
(
name
,
func
)
214
end
215 216
function
callback
.
register
(
name
,
func
)
-- original
217
if
not
frozen
[
name
]
then
218
return
register_callback
(
name
,
func
)
219
elseif
permit_overloads
then
220
return
register_usercall
(
"
registering
"
,
name
,
func
)
221
else
222
return
frozen_callback
(
name
)
223
end
224
end
225 226
function
callbacks
.
push
(
name
,
func
)
227
if
not
frozen
[
name
]
or
permit_overloads
then
228
local
sn
=
stack
[
name
]
229
if
not
sn
then
230
sn
=
{
}
231
stack
[
name
]
=
sn
232
end
233
insert
(
sn
,
find_callback
(
name
)
)
234
if
permit_overloads
then
235
register_usercall
(
"
pushing
"
,
name
,
func
)
236
else
237
register_callback
(
name
,
func
)
238
end
239
else
240
report_callbacks
(
"
not %s frozen %a
"
,
"
pushing
"
,
name
)
241
end
242
end
243 244
function
callbacks
.
pop
(
name
)
245
if
not
frozen
[
name
]
or
permit_overloads
then
246
local
sn
=
stack
[
name
]
247
if
not
sn
or
#
sn
=
=
0
then
248
-- some error
249
register_callback
(
name
,
nil
)
-- ! really needed
250
else
251
-- this fails: register_callback(name, remove(stack[name]))
252
local
func
=
remove
(
sn
)
253
register_callback
(
name
,
func
)
254
end
255
end
256
end
257 258
if
trace_calls
then
259
statistics
.
register
(
"
callback details
"
,
function
(
)
260
local
t
=
{
}
-- todo: pass function to register and quit at nil
261
for
name
,
n
in
sortedhash
(
list
)
do
262
if
n
>
0
then
263
t
[
#
t
+
1
]
=
format
(
"
%s -> %s
"
,
name
,
n
)
264
end
265
end
266
return
concat
(
t
,
"
"
)
267
end
)
268
end
269 270
statistics
.
register
(
"
callbacks overloaded by user
"
,
function
(
)
271
if
permit_overloads
then
272
return
concat
(
sortedkeys
(
permit_overloads
)
,
"
"
)
273
end
274
end
)
275 276
-- -- somehow crashes later on
277
--
278
-- callbacks.freeze("find_.*_file","finding file")
279
-- callbacks.freeze("read_.*_file","reading file")
280
-- callbacks.freeze("open_.*_file","opening file")
281 282
--[[ldx-- 283<p>The simple case is to remove the callback:</p> 284 285<code> 286callbacks.push('linebreak_filter') 287... some actions ... 288callbacks.pop('linebreak_filter') 289</code> 290 291<p>Often, in such case, another callback or a macro call will pop 292the original.</p> 293 294<p>In practice one will install a new handler, like in:</p> 295 296<code> 297callbacks.push('linebreak_filter', function(...) 298 return something_done(...) 299end) 300</code> 301 302<p>Even more interesting is:</p> 303 304<code> 305callbacks.push('linebreak_filter', function(...) 306 callbacks.pop('linebreak_filter') 307 return something_done(...) 308end) 309</code> 310 311<p>This does a one-shot.</p> 312--ldx]]
--
313 314
--[[ldx-- 315<p>Callbacks may result in <l n='lua'/> doing some hard work 316which takes time and above all resourses. Sometimes it makes 317sense to disable or tune the garbage collector in order to 318keep the use of resources acceptable.</p> 319 320<p>At some point in the development we did some tests with counting 321nodes (in this case 121049).</p> 322 323<table> 324<tr><td>setstepmul</td><td>seconds</td><td>megabytes</td></tr> 325<tr><td>200</td><td>24.0</td><td>80.5</td></tr> 326<tr><td>175</td><td>21.0</td><td>78.2</td></tr> 327<tr><td>150</td><td>22.0</td><td>74.6</td></tr> 328<tr><td>160</td><td>22.0</td><td>74.6</td></tr> 329<tr><td>165</td><td>21.0</td><td>77.6</td></tr> 330<tr><td>125</td><td>21.5</td><td>89.2</td></tr> 331<tr><td>100</td><td>21.5</td><td>88.4</td></tr> 332</table> 333 334<p>The following code is kind of experimental. In the documents 335that describe the development of <l n='luatex'/> we report 336on speed tests. One observation is that it sometimes helps to 337restart the collector. Okay, experimental code has been removed, 338because messing aroudn with the gc is too unpredictable.</p> 339--ldx]]
--
340 341
-- For the moment we keep this here and not in util-gbc.lua or so.
342 343
utilities
=
utilities
or
{
}
344
utilities
.
garbagecollector
=
utilities
.
garbagecollector
or
{
}
345
local
garbagecollector
=
utilities
.
garbagecollector
346 347
garbagecollector
.
enabled
=
false
-- could become a directive
348
garbagecollector
.
criterium
=
4
*
1024
*
1024
349 350
-- garbagecollector.enabled = true
351 352
-- Lua allocates up to 12 times the amount of memory needed for
353
-- handling a string, and for large binary chunks (like chinese otf
354
-- files) we get a prominent memory consumption. Even when a variable
355
-- is nilled, there is some delay in freeing the associated memory (the
356
-- hashed string) because if we do the same thing directly afterwards,
357
-- we see only a slight increase in memory. For that reason it makes
358
-- sense to do a collector pass after a huge file.
359
--
360
-- test file:
361
--
362
-- function test()
363
-- local b = collectgarbage("count")
364
-- local s = io.loaddata("some font table, e.g. a big tmc file")
365
-- local a = collectgarbage("count")
366
-- print(">>> STATUS",b,a,a-b,#s,1000*(a-b)/#s)
367
-- end
368
--
369
-- test() test() test() test() collectgarbage("collect") test() test() test() test()
370
--
371
-- As a result of this, LuaTeX now uses an optimized version of f:read("*a"),
372
-- one that does not use the 4K allocations but allocates in one step.
373 374
function
garbagecollector
.
check
(
size
,
criterium
)
375
if
garbagecollector
.
enabled
then
376
criterium
=
criterium
or
garbagecollector
.
criterium
377
if
not
size
or
(
criterium
and
criterium
>
0
and
size
>
criterium
)
then
378
if
trace_checking
then
379
local
b
=
collectgarbage
(
"
count
"
)
380
collectgarbage
(
"
collect
"
)
381
local
a
=
collectgarbage
(
"
count
"
)
382
report_memory
(
"
forced sweep, collected: %s MB, used: %s MB
"
,
round
(
(
b
-
a
)
/
1000
)
,
round
(
a
/
1000
)
)
383
else
384
collectgarbage
(
"
collect
"
)
385
end
386
end
387
end
388
end
389 390
-- this will move to a module
391 392
commands
=
commands
or
{
}
393 394
function
commands
.
showcallbacks
(
)
395
local
NC
,
NR
,
verbatim
=
context
.
NC
,
context
.
NR
,
context
.
type
396
context
.
starttabulate
{
"
|l|l|p|
"
}
397
for
name
,
_
in
sortedhash
(
list
)
do
398
NC
(
)
verbatim
(
name
)
NC
(
)
verbatim
(
state
(
name
)
)
NC
(
)
context
(
frozen
[
name
]
or
"
"
)
NC
(
)
NR
(
)
399
end
400
context
.
stoptabulate
(
)
401
end
402