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