node-res.lua /size: 18 Kb    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['node-res'] = {
2    version   = 1.001,
3    comment   = "companion to node-ini.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
9local type, next = type, next
10local gmatch, format = string.gmatch, string.format
11
12--[[ldx--
13<p>The next function is not that much needed but in <l n='context'/> we use
14for debugging <l n='luatex'/> node management.</p>
15--ldx]]--
16
17local nodes, node = nodes, node
18
19local report_nodes   = logs.reporter("nodes","housekeeping")
20
21nodes.pool           = nodes.pool or { }
22local nodepool       = nodes.pool
23
24local whatsitcodes   = nodes.whatsitcodes
25local gluecodes      = nodes.gluecodes
26local kerncodes      = nodes.kerncodes
27local rulecodes      = nodes.rulecodes
28local nodecodes      = nodes.nodecodes
29local boundarycodes  = nodes.boundarycodes
30local usercodes      = nodes.usercodes
31
32local nodeproperties = nodes.properties.data
33
34local glyph_code     = nodecodes.glyph
35local rule_code      = nodecodes.rule
36local kern_code      = nodecodes.kern
37local glue_code      = nodecodes.glue
38local gluespec_code  = nodecodes.gluespec
39local whatsit_code   = nodecodes.whatsit
40
41local currentfont    = font.current
42local texgetcount    = tex.getcount
43
44local allocate       = utilities.storage.allocate
45
46local reserved       = { }
47local nofreserved    = 0
48local userids        = allocate()
49local lastid         = 0
50
51setmetatable(userids, {
52    __index = function(t,k)
53        if type(k) == "string" then
54            lastid = lastid + 1
55            rawset(userids,lastid,k)
56            rawset(userids,k,lastid)
57            return lastid
58        else
59            rawset(userids,k,k)
60            return k
61        end
62    end,
63    __call = function(t,k)
64        return t[k]
65    end
66} )
67
68-- nuts overload
69
70local nuts         = nodes.nuts
71local nutpool      = { }
72nuts.pool          = nutpool
73
74local tonut        = nuts.tonut
75local tonode       = nuts.tonode
76
77local getbox       = nuts.getbox
78local getid        = nuts.getid
79local getlist      = nuts.getlist
80local getglue      = nuts.getglue
81
82local setfield     = nuts.setfield
83local setchar      = nuts.setchar
84local setlist      = nuts.setlist
85local setwhd       = nuts.setwhd
86local setglue      = nuts.setglue
87local setdisc      = nuts.setdisc
88local setfont      = nuts.setfont
89local setkern      = nuts.setkern
90local setpenalty   = nuts.setpenalty
91local setdir       = nuts.setdir
92local setdirection = nuts.setdirection
93local setshift     = nuts.setshift
94local setwidth     = nuts.setwidth
95local setsubtype   = nuts.setsubtype
96local setleader    = nuts.setleader
97
98local setdata      = nuts.setdata
99local setruledata  = nuts.setruledata
100local setvalue     = nuts.setvalue
101
102local copy_nut     = nuts.copy
103local new_nut      = nuts.new
104local flush_nut    = nuts.flush
105
106-- at some point we could have a dual set (the overhead of tonut is not much larger than
107-- metatable associations at the lua/c end esp if we also take assignments into account
108
109-- table.setmetatableindex(nodepool,function(t,k,v)
110--  -- report_nodes("defining nodepool[%s] instance",k)
111--     local f = nutpool[k]
112--     local v = function(...)
113--         return tonode(f(...))
114--     end
115--     t[k] = v
116--     return v
117-- end)
118--
119-- -- we delay one step because that permits us a forward reference
120-- -- e.g. in pdfsetmatrix
121
122table.setmetatableindex(nodepool,function(t,k,v)
123 -- report_nodes("defining nodepool[%s] instance",k)
124    local v = function(...)
125        local f = nutpool[k]
126        local v = function(...)
127            return tonode(f(...))
128        end
129        t[k] = v
130        return v(...)
131    end
132    t[k] = v
133    return v
134end)
135
136local function register_nut(n)
137    nofreserved = nofreserved + 1
138    reserved[nofreserved] = n
139    return n
140end
141
142local function register_node(n)
143    nofreserved = nofreserved + 1
144    if type(n) == "number" then -- isnut(n)
145        reserved[nofreserved] = n
146    else
147        reserved[nofreserved] = tonut(n)
148    end
149    return n
150end
151
152nodepool.userids  = userids
153nodepool.register = register_node
154
155nutpool.userids   = userids
156nutpool.register  = register_node -- could be register_nut
157
158-- so far
159
160local disc              = register_nut(new_nut(nodecodes.disc))
161local kern              = register_nut(new_nut(kern_code,kerncodes.userkern))
162local fontkern          = register_nut(new_nut(kern_code,kerncodes.fontkern))
163local italickern        = register_nut(new_nut(kern_code,kerncodes.italiccorrection))
164local penalty           = register_nut(new_nut(nodecodes.penalty))
165local glue              = register_nut(new_nut(glue_code))
166local gluespec          = register_nut(new_nut(gluespec_code))
167local glyph             = register_nut(new_nut(glyph_code,0))
168
169local textdir           = register_nut(new_nut(nodecodes.dir))
170
171local latelua           = register_nut(new_nut(whatsit_code,whatsitcodes.latelua))
172local savepos           = register_nut(new_nut(whatsit_code,whatsitcodes.savepos))
173
174local user_node         = new_nut(whatsit_code,whatsitcodes.userdefined)
175
176setfield(user_node,"type",usercodes.number)
177
178local left_margin_kern  = register_nut(new_nut(nodecodes.marginkern,0))
179local right_margin_kern = register_nut(new_nut(nodecodes.marginkern,1))
180
181local lineskip          = register_nut(new_nut(glue_code,gluecodes.lineskip))
182local baselineskip      = register_nut(new_nut(glue_code,gluecodes.baselineskip))
183local leftskip          = register_nut(new_nut(glue_code,gluecodes.leftskip))
184local rightskip         = register_nut(new_nut(glue_code,gluecodes.rightskip))
185local lefthangskip      = register_nut(new_nut(glue_code,gluecodes.lefthangskip))
186local righthangskip     = register_nut(new_nut(glue_code,gluecodes.righthangskip))
187local indentskip        = register_nut(new_nut(glue_code,gluecodes.indentskip))
188local correctionskip    = register_nut(new_nut(glue_code,gluecodes.correctionskip))
189
190local temp              = register_nut(new_nut(nodecodes.temp,0))
191
192local noad              = register_nut(new_nut(nodecodes.noad))
193local delimiter         = register_nut(new_nut(nodecodes.delimiter))
194local fence             = register_nut(new_nut(nodecodes.fence))
195local submlist          = register_nut(new_nut(nodecodes.submlist))
196local accent            = register_nut(new_nut(nodecodes.accent))
197local radical           = register_nut(new_nut(nodecodes.radical))
198local fraction          = register_nut(new_nut(nodecodes.fraction))
199local subbox            = register_nut(new_nut(nodecodes.subbox))
200local mathchar          = register_nut(new_nut(nodecodes.mathchar))
201local mathtextchar      = register_nut(new_nut(nodecodes.mathtextchar))
202local choice            = register_nut(new_nut(nodecodes.choice))
203
204local boundary          = register_nut(new_nut(nodecodes.boundary,boundarycodes.user))
205local wordboundary      = register_nut(new_nut(nodecodes.boundary,boundarycodes.word))
206
207local cleader           = register_nut(copy_nut(glue)) setsubtype(cleader,gluecodes.cleaders) setglue(cleader,0,65536,0,2,0)
208
209-- the dir field needs to be set otherwise crash:
210
211local lefttoright_code  = nodes.dirvalues.lefttoright
212
213local rule              = register_nut(new_nut(rule_code))                   setdirection(rule, lefttoright_code)
214local emptyrule         = register_nut(new_nut(rule_code,rulecodes.empty))   setdirection(rule, lefttoright_code)
215local userrule          = register_nut(new_nut(rule_code,rulecodes.user))    setdirection(rule, lefttoright_code)
216local outlinerule       = register_nut(new_nut(rule_code,rulecodes.outline)) setdirection(rule, lefttoright_code)
217local hlist             = register_nut(new_nut(nodecodes.hlist))             setdirection(hlist,lefttoright_code)
218local vlist             = register_nut(new_nut(nodecodes.vlist))             setdirection(vlist,lefttoright_code)
219
220function nutpool.glyph(fnt,chr)
221    local n = copy_nut(glyph)
222    if fnt then
223        setfont(n,fnt == true and currentfont() or fnt,chr)
224    elseif chr then
225        setchar(n,chr)
226    end
227    return n
228end
229
230function nutpool.penalty(p)
231    local n = copy_nut(penalty)
232    if p and p ~= 0 then
233        setpenalty(n,p)
234    end
235    return n
236end
237
238function nutpool.kern(k)
239    local n = copy_nut(kern)
240    if k and k ~= 0 then
241        setkern(n,k)
242    end
243    return n
244end
245
246function nutpool.boundary(v)
247    local n = copy_nut(boundary)
248    if v and v ~= 0 then
249        setvalue(n,v)
250    end
251    return n
252end
253
254function nutpool.wordboundary(v)
255    local n = copy_nut(wordboundary)
256    if v and v ~= 0 then
257        setvalue(n,v)
258    end
259    return n
260end
261
262function nutpool.fontkern(k)
263    local n = copy_nut(fontkern)
264    if k and k ~= 0 then
265        setkern(n,k)
266    end
267    return n
268end
269
270function nutpool.italickern(k)
271    local n = copy_nut(italickern)
272    if k and k ~= 0 then
273        setkern(n,k)
274    end
275    return n
276end
277
278function nutpool.gluespec(width,stretch,shrink,stretch_order,shrink_order)
279    local n = copy_nut(gluespec)
280    if width or stretch or shrink or stretch_order or shrink_order then
281        setglue(n,width,stretch,shrink,stretch_order,shrink_order)
282    end
283    return n
284end
285
286local function someskip(skip,width,stretch,shrink,stretch_order,shrink_order)
287    -- maybe setglue
288    local n = copy_nut(skip)
289    if width or stretch or shrink or stretch_order or shrink_order then
290        setglue(n,width,stretch,shrink,stretch_order,shrink_order)
291    end
292    return n
293end
294
295function nutpool.stretch(a,b)
296    -- width stretch shrink stretch_order shrink_order
297    local n = copy_nut(glue)
298    if not b then
299        a, b = 1, a or 1
300    end
301    setglue(n,0,a,0,b,0)
302    return n
303end
304
305function nutpool.shrink(a,b)
306    local n = copy_nut(glue)
307    if not b then
308        a, b = 1, a or 1
309    end
310    setglue(n,0,0,a,0,0,b)
311    return n
312end
313
314function nutpool.glue(width,stretch,shrink,stretch_order,shrink_order)
315    return someskip(glue,width,stretch,shrink,stretch_order,shrink_order)
316end
317
318function nutpool.negatedglue(glue)
319    local n = copy_nut(glue)
320    local width, stretch, shrink = getglue(n)
321    setglue(n,-width,-stretch,-shrink)
322    return n
323end
324
325function nutpool.leftskip(width,stretch,shrink,stretch_order,shrink_order)
326    return someskip(leftskip,width,stretch,shrink,stretch_order,shrink_order)
327end
328
329function nutpool.rightskip(width,stretch,shrink,stretch_order,shrink_order)
330    return someskip(rightskip,width,stretch,shrink,stretch_order,shrink_order)
331end
332
333function nutpool.lefthangskip(width,stretch,shrink,stretch_order,shrink_order)
334    return someskip(lefthangskip,width,stretch,shrink,stretch_order,shrink_order)
335end
336
337function nutpool.righthangskip(width,stretch,shrink,stretch_order,shrink_order)
338    return someskip(righthangskip,width,stretch,shrink,stretch_order,shrink_order)
339end
340
341function nutpool.indentskip(width,stretch,shrink,stretch_order,shrink_order)
342    return someskip(indentskip,width,stretch,shrink,stretch_order,shrink_order)
343end
344
345function nutpool.lineskip(width,stretch,shrink,stretch_order,shrink_order)
346    return someskip(lineskip,width,stretch,shrink,stretch_order,shrink_order)
347end
348
349function nutpool.baselineskip(width,stretch,shrink)
350    return someskip(baselineskip,width,stretch,shrink)
351end
352
353function nutpool.disc(pre,post,replace)
354    local d = copy_nut(disc)
355    if pre or post or replace then
356        setdisc(d,pre,post,replace)
357    end
358    return d
359end
360
361function nutpool.direction(dir,swap)
362    local t = copy_nut(textdir)
363    if not dir then
364        -- just a l2r start node
365    elseif swap then
366        setdirection(t,dir,true)
367    else
368        setdirection(t,dir,false)
369    end
370    return t
371end
372
373function nutpool.rule(width,height,depth) -- w/h/d == nil will let them adapt
374    local n = copy_nut(rule)
375    if width or height or depth then
376        setwhd(n,width,height,depth)
377    end
378    return n
379end
380
381function nutpool.emptyrule(width,height,depth) -- w/h/d == nil will let them adapt
382    local n = copy_nut(emptyrule)
383    if width or height or depth then
384        setwhd(n,width,height,depth)
385    end
386    return n
387end
388
389function nutpool.userrule(width,height,depth) -- w/h/d == nil will let them adapt
390    local n = copy_nut(userrule)
391    if width or height or depth then
392        setwhd(n,width,height,depth)
393    end
394    return n
395end
396
397function nutpool.outlinerule(width,height,depth,line) -- w/h/d == nil will let them adapt
398    local n = copy_nut(outlinerule)
399    if width or height or depth then
400        setwhd(n,width,height,depth)
401    end
402    if line then
403        setruledata(n,line)
404    end
405    return n
406end
407
408function nutpool.leader(width,list)
409    local n = copy_nut(cleader)
410    if width then
411        setwidth(n,width)
412    end
413    if list then
414        setleader(n,list)
415    end
416    return n
417end
418
419function nutpool.savepos()
420    return copy_nut(savepos)
421end
422
423function nutpool.latelua(code)
424    local n = copy_nut(latelua)
425    if type(code) == "table" then
426        local action        = code.action
427        local specification = code.specification or code
428        code = function() action(specification) end
429    end
430    setdata(n,code)
431    return n
432end
433
434function nutpool.leftmarginkern(glyph,width)
435    local n = copy_nut(left_margin_kern)
436    if not glyph then
437        report_nodes("invalid pointer to left margin glyph node")
438    elseif getid(glyph) ~= glyph_code then
439        report_nodes("invalid node type %a for %s margin glyph node",nodecodes[glyph],"left")
440    else
441        setfield(n,"glyph",glyph)
442    end
443    if width and width ~= 0 then
444        setwidth(n,width)
445    end
446    return n
447end
448
449function nutpool.rightmarginkern(glyph,width)
450    local n = copy_nut(right_margin_kern)
451    if not glyph then
452        report_nodes("invalid pointer to right margin glyph node")
453    elseif getid(glyph) ~= glyph_code then
454        report_nodes("invalid node type %a for %s margin glyph node",nodecodes[p],"right")
455    else
456        setfield(n,"glyph",glyph)
457    end
458    if width and width ~= 0 then
459        setwidth(n,width)
460    end
461    return n
462end
463
464function nutpool.temp()
465    return copy_nut(temp)
466end
467
468function nutpool.noad()         return copy_nut(noad)         end
469function nutpool.delimiter()    return copy_nut(delimiter)    end  nutpool.delim = nutpool.delimiter
470function nutpool.fence()        return copy_nut(fence)        end
471function nutpool.submlist()     return copy_nut(submlist)     end
472function nutpool.noad()         return copy_nut(noad)         end
473function nutpool.fence()        return copy_nut(fence)        end
474function nutpool.accent()       return copy_nut(accent)       end
475function nutpool.radical()      return copy_nut(radical)      end
476function nutpool.fraction()     return copy_nut(fraction)     end
477function nutpool.subbox()       return copy_nut(subbox)       end
478function nutpool.mathchar()     return copy_nut(mathchar)     end
479function nutpool.mathtextchar() return copy_nut(mathtextchar) end
480function nutpool.choice()       return copy_nut(choice)       end
481
482local function new_hlist(list,width,height,depth,shift)
483    local n = copy_nut(hlist)
484    if list then
485        setlist(n,list)
486    end
487    if width or height or depth then
488        setwhd(n,width,height,depth)
489    end
490    if shift and shift ~= 0 then
491        setshift(n,shift)
492    end
493    return n
494end
495
496local function new_vlist(list,width,height,depth,shift)
497    local n = copy_nut(vlist)
498    if list then
499        setlist(n,list)
500    end
501    if width or height or depth then
502        setwhd(n,width,height,depth)
503    end
504    if shift and shift ~= 0 then
505        setshift(n,shift)
506    end
507    return n
508end
509
510nutpool.hlist = new_hlist
511nutpool.vlist = new_vlist
512
513function nodepool.hlist(list,width,height,depth,shift)
514    return tonode(new_hlist(list and tonut(list),width,height,depth,shift))
515end
516
517function nodepool.vlist(list,width,height,depth,shift)
518    return tonode(new_vlist(list and tonut(list),width,height,depth,shift))
519end
520
521function nutpool.usernode(id,data)
522    local n = copy_nut(user_node)
523    nodeproperties[n] = {
524        id   = id,
525        data = data,
526    }
527    return n
528end
529
530-- housekeeping
531
532local function cleanup(nofboxes) -- todo
533    local tracers = nodes.tracers
534    if tracers and tracers.steppers then -- to be resolved
535        tracers.steppers.reset() -- todo: make a registration subsystem
536    end
537    local nl = 0
538    local nr = nofreserved
539    for i=1,nofreserved do
540        local ri = reserved[i]
541        flush_nut(reserved[i])
542    end
543    if nofboxes then
544        for i=0,nofboxes do
545            local l = getbox(i)
546            if l then
547                flush_nut(l) -- also list ?
548                nl = nl + 1
549            end
550        end
551    end
552    reserved    = { }
553    nofreserved = 0
554    return nr, nl, nofboxes -- can be nil
555end
556
557local function usage()
558    local t = { }
559    for n, tag in gmatch(status.node_mem_usage,"(%d+) ([a-z_]+)") do
560        t[tag] = tonumber(n) or 0
561    end
562    return t
563end
564
565local stock = { }
566
567nutpool .cleanup = cleanup
568nodepool.cleanup = cleanup
569
570nutpool .usage   = usage
571nodepool.usage   = usage
572
573nutpool .stock   = stock
574nodepool.stock   = stock
575
576-- end
577
578statistics.register("cleaned up reserved nodes", function()
579    return format("%s nodes, %s lists of %s", cleanup(texgetcount("c_syst_last_allocated_box")))
580end) -- \topofboxstack
581
582statistics.register("node memory usage", function() -- comes after cleanup !
583    local used = usage()
584    if next(used) then
585        local t, n = { }, 0
586        for k, v in table.sortedhash(used) do
587            n = n + 1 ; t[n] = format("%s %s",v,k)
588        end
589        return table.concat(t,", ")
590    end
591end)
592
593lua.registerinitexfinalizer(cleanup, "cleanup reserved nodes")
594
595-- experiment
596
597do
598
599    local glyph      = tonode(glyph)
600    local traverseid = nodes.traverseid
601
602    local traversers = table.setmetatableindex(function(t,k)
603        local v = traverseid(type(k) == "number" and k or nodecodes[k],glyph)
604        t[k] = v
605        return v
606    end)
607
608                                traversers.node  = nodes.traverse     (glyph)
609                                traversers.char  = nodes.traversechar (glyph)
610    if nodes.traverseglyph then traversers.glyph = nodes.traverseglyph(glyph) end
611    if nodes.traverselist  then traversers.list  = nodes.traverselist (glyph) end
612
613    nodes.traversers = traversers
614
615end
616
617do
618
619    local glyph      = glyph
620    local traverseid = nuts.traverseid
621
622    local traversers = table.setmetatableindex(function(t,k)
623        local v = traverseid(type(k) == "number" and k or nodecodes[k],glyph)
624        t[k] = v
625        return v
626    end)
627
628                                 traversers.node    = nuts.traverse       (glyph)
629                                 traversers.char    = nuts.traversechar   (glyph)
630    if nuts.traverseglyph   then traversers.glyph   = nuts.traverseglyph  (glyph) end
631    if nuts.traverselist    then traversers.list    = nuts.traverselist   (glyph) end
632    if nuts.traversecontent then traversers.content = nuts.traversecontent(glyph) end
633
634    nuts.traversers = traversers
635
636end
637