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