node-res.lmt /size: 18 Kb    last modification: 2024-01-16 09: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     = nodecodes.glyph
30local rule_code      = nodecodes.rule
31local kern_code      = nodecodes.kern
32local glue_code      = nodecodes.glue
33local gluespec_code  = 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 italickern        = 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))
174
175local cleader           = register_nut(copy_nut(glue)) setsubtype(cleader,gluecodes.cleaders) setglue(cleader,0,65536,0,2,0)
176
177-- the dir field needs to be set otherwise crash:
178
179local lefttoright_code  = tex.directioncodes.lefttoright
180
181local rule              = register_nut(new_nut(rule_code))                   -- setdirection(rule, lefttoright_code)
182local emptyrule         = register_nut(new_nut(rule_code,rulecodes.empty))   -- setdirection(rule, lefttoright_code)
183local strutrule         = register_nut(new_nut(rule_code,rulecodes.strut))   -- 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 virtualrule       = register_nut(new_nut(rule_code,rulecodes.virtual)) -- setdirection(rule, lefttoright_code)
189local hlist             = register_nut(new_nut(nodecodes.hlist))                setdirection(hlist,lefttoright_code)
190local vlist             = register_nut(new_nut(nodecodes.vlist))                setdirection(vlist,lefttoright_code)
191
192function nutpool.glyph(fnt,chr)
193    local n = copy_nut(glyph)
194    if fnt then
195        setfont(n,fnt == true and currentfont() or fnt,chr)
196    elseif chr then
197        setchar(n,chr)
198    end
199    return n
200end
201
202function nutpool.penalty(p)
203    local n = copy_nut(penalty)
204    if p and p ~= 0 then
205        setpenalty(n,p)
206    end
207    return n
208end
209
210function nutpool.kern(k)
211    local n = copy_nut(kern)
212    if k and k ~= 0 then
213        setkern(n,k)
214    end
215    return n
216end
217
218function nutpool.boundary(v)
219    local n = copy_nut(boundary)
220    if v and v ~= 0 then
221        setvalue(n,v)
222    end
223    return n
224end
225
226function nutpool.wordboundary(v)
227    local n = copy_nut(wordboundary)
228    if v and v ~= 0 then
229        setvalue(n,v)
230    end
231    return n
232end
233
234function nutpool.fontkern(k)
235    local n = copy_nut(fontkern)
236    if k and k ~= 0 then
237        setkern(n,k)
238    end
239    return n
240end
241
242function nutpool.italickern(k)
243    local n = copy_nut(italickern)
244    if k and k ~= 0 then
245        setkern(n,k)
246    end
247    return n
248end
249
250function nutpool.spacefontkern(k)
251    local n = copy_nut(spacefontkern)
252    if k and k ~= 0 then
253        setkern(n,k)
254    end
255    return n
256end
257
258function nutpool.gluespec(width,stretch,shrink,stretch_order,shrink_order)
259    local n = copy_nut(gluespec)
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
266local function someskip(skip,width,stretch,shrink,stretch_order,shrink_order)
267    -- maybe setglue
268    local n = copy_nut(skip)
269    if width or stretch or shrink or stretch_order or shrink_order then
270        setglue(n,width,stretch,shrink,stretch_order,shrink_order)
271    end
272    return n
273end
274
275function nutpool.stretch(a,b)
276    -- width stretch shrink stretch_order shrink_order
277    local n = copy_nut(glue)
278    if not b then
279        a, b = 1, a or 1
280    end
281    setglue(n,0,a,0,b,0)
282    return n
283end
284
285function nutpool.shrink(a,b)
286    local n = copy_nut(glue)
287    if not b then
288        a, b = 1, a or 1
289    end
290    setglue(n,0,0,a,0,0,b)
291    return n
292end
293
294function nutpool.glue(width,stretch,shrink,stretch_order,shrink_order)
295    return someskip(glue,width,stretch,shrink,stretch_order,shrink_order)
296end
297
298function nutpool.negatedglue(glue)
299    local n = copy_nut(glue)
300    local width, stretch, shrink = getglue(n)
301    setglue(n,-width,-stretch,-shrink)
302    return n
303end
304
305function nutpool.leftskip(width,stretch,shrink,stretch_order,shrink_order)
306    return someskip(leftskip,width,stretch,shrink,stretch_order,shrink_order)
307end
308
309function nutpool.rightskip(width,stretch,shrink,stretch_order,shrink_order)
310    return someskip(rightskip,width,stretch,shrink,stretch_order,shrink_order)
311end
312
313function nutpool.lefthangskip(width,stretch,shrink,stretch_order,shrink_order)
314    return someskip(lefthangskip,width,stretch,shrink,stretch_order,shrink_order)
315end
316
317function nutpool.righthangskip(width,stretch,shrink,stretch_order,shrink_order)
318    return someskip(righthangskip,width,stretch,shrink,stretch_order,shrink_order)
319end
320
321function nutpool.indentskip(width,stretch,shrink,stretch_order,shrink_order)
322    return someskip(indentskip,width,stretch,shrink,stretch_order,shrink_order)
323end
324
325function nutpool.lineskip(width,stretch,shrink,stretch_order,shrink_order)
326    return someskip(lineskip,width,stretch,shrink,stretch_order,shrink_order)
327end
328
329function nutpool.baselineskip(width,stretch,shrink)
330    return someskip(baselineskip,width,stretch,shrink)
331end
332
333function nutpool.disc(pre,post,replace)
334    local d = copy_nut(disc)
335    if pre or post or replace then
336        setdisc(d,pre,post,replace)
337    end
338    return d
339end
340
341function nutpool.direction(dir,swap)
342    local t = copy_nut(textdir)
343    if not dir then
344        -- just a l2r start node
345    elseif swap then
346        setdirection(t,dir,true)
347    else
348        setdirection(t,dir,false)
349    end
350    return t
351end
352
353function nutpool.rule(width,height,depth) -- w/h/d == nil will let them adapt
354    local n = copy_nut(rule)
355    if width or height or depth then
356        setwhd(n,width,height,depth)
357    end
358    return n
359end
360
361function nutpool.emptyrule(width,height,depth) -- w/h/d == nil will let them adapt
362    local n = copy_nut(emptyrule)
363    if width or height or depth then
364        setwhd(n,width,height,depth)
365    end
366    return n
367end
368
369-- data left right
370
371function nutpool.strutrule(width,height,depth) -- w/h/d == nil will let them adapt
372    local n = copy_nut(strutrule)
373    if width or height or depth then
374        setwhd(n,width,height,depth)
375    end
376    return n
377end
378
379function nutpool.userrule(width,height,depth) -- w/h/d == nil will let them adapt
380    local n = copy_nut(userrule)
381    if width or height or depth then
382        setwhd(n,width,height,depth)
383    end
384    return n
385end
386
387function nutpool.outlinerule(width,height,depth,line) -- w/h/d == nil will let them adapt
388    local n = copy_nut(outlinerule)
389    if width or height or depth then
390        setwhd(n,width,height,depth)
391    end
392    if line then
393        setdata(n,round(line)) -- has to be an integer
394    end
395    return n
396end
397
398function nutpool.imagerule(width,height,depth) -- w/h/d == nil will let them adapt
399    local n = copy_nut(imagerule)
400    if width or height or depth then
401        setwhd(n,width,height,depth)
402    end
403    return n
404end
405
406function nutpool.boxrule(width,height,depth) -- w/h/d == nil will let them adapt
407    local n = copy_nut(boxrule)
408    if width or height or depth then
409        setwhd(n,width,height,depth)
410    end
411    return n
412end
413
414function nutpool.virtualrule(width,height,depth,data)
415    local n = copy_nut(virtualrule)
416    if width or height or depth or data then
417        setruledimensions(n,width,height,depth,data)
418    end
419    return n
420end
421
422local function new_leader(width,list)
423    local n = copy_nut(cleader)
424    if width then
425        setwidth(n,width)
426    end
427    if list then
428        setleader(n,list)
429    end
430    return n
431end
432
433nutpool.leader = new_leader
434
435function nodepool.leader(width,list)
436    return tonode(new_leader(width,list and tonut(list)))
437end
438
439function nutpool.leftmarginkern(glyph,width)
440    local n = copy_nut(left_margin_kern)
441    if not glyph then
442        report_nodes("invalid pointer to left margin glyph node")
443    elseif getid(glyph) ~= glyph_code then
444        report_nodes("invalid node type %a for %s margin glyph node",nodecodes[glyph],"left")
445    else
446        setfield(n,"glyph",glyph)
447    end
448    if width and width ~= 0 then
449        setwidth(n,width)
450    end
451    return n
452end
453
454function nutpool.rightmarginkern(glyph,width)
455    local n = copy_nut(right_margin_kern)
456    if not glyph then
457        report_nodes("invalid pointer to right margin glyph node")
458    elseif getid(glyph) ~= glyph_code then
459        report_nodes("invalid node type %a for %s margin glyph node",nodecodes[p],"right")
460    else
461        setfield(n,"glyph",glyph)
462    end
463    if width and width ~= 0 then
464        setwidth(n,width)
465    end
466    return n
467end
468
469function nutpool.temp()
470    return copy_nut(temp)
471end
472
473function nutpool.noad(class)
474    local n = copy_nut(noad)
475    if class then
476        setsubtype(n,class)
477        setclass(n,class,class,class)
478    end
479    return n
480end
481
482-- maybe also the rest wrt subtype and class
483
484function nutpool.delimiter()    return copy_nut(delimiter)    end  nutpool.delim = nutpool.delimiter
485function nutpool.fence()        return copy_nut(fence)        end
486function nutpool.submlist()     return copy_nut(submlist)     end
487function nutpool.fence()        return copy_nut(fence)        end
488function nutpool.accent()       return copy_nut(accent)       end
489function nutpool.radical()      return copy_nut(radical)      end
490function nutpool.fraction()     return copy_nut(fraction)     end
491function nutpool.subbox()       return copy_nut(subbox)       end
492function nutpool.mathchar()     return copy_nut(mathchar)     end
493function nutpool.mathtextchar() return copy_nut(mathtextchar) end
494function nutpool.choice()       return copy_nut(choice)       end
495
496local function new_hlist(list,width,height,depth,shift)
497    local n = copy_nut(hlist)
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
510local function new_vlist(list,width,height,depth,shift)
511    local n = copy_nut(vlist)
512    if list then
513        setlist(n,list)
514    end
515    if width or height or depth then
516        setwhd(n,width,height,depth)
517    end
518    if shift and shift ~= 0 then
519        setshift(n,shift)
520    end
521    return n
522end
523
524nutpool.hlist = new_hlist
525nutpool.vlist = new_vlist
526
527function nodepool.hlist(list,width,height,depth,shift)
528    return tonode(new_hlist(list and tonut(list),width,height,depth,shift))
529end
530
531function nodepool.vlist(list,width,height,depth,shift)
532    return tonode(new_vlist(list and tonut(list),width,height,depth,shift))
533end
534
535function nutpool.usernode(id,data)
536    local n = copy_nut(user_node)
537    nodeproperties[n] = {
538        id   = id,
539        data = data,
540    }
541    return n
542end
543
544-- housekeeping
545
546local function cleanup(nofboxes) -- todo
547    -- this is bonus, not really needed
548    local tracers = nodes.tracers
549    if tracers and tracers.steppers then -- to be resolved
550        tracers.steppers.reset() -- todo: make a registration subsystem
551    end
552    local nl = 0
553    local nr = nofreserved
554    for i=1,nofreserved do
555        local ri = reserved[i]
556        flush_nut(reserved[i])
557    end
558    if nofboxes then
559        for i=0,nofboxes do
560            local l = getbox(i)
561            if l then
562                flush_nut(l) -- also list ?
563                nl = nl + 1
564            end
565        end
566    end
567    reserved    = { }
568    nofreserved = 0
569    return nr, nl, nofboxes -- can be nil
570end
571
572local usage = node.inuse
573local stock = node.instock
574
575nutpool .cleanup = cleanup
576nodepool.cleanup = cleanup
577
578nutpool .usage   = usage
579nodepool.usage   = usage
580
581nutpool .stock   = stock
582nodepool.stock   = stock
583
584-- end
585
586statistics.register("cleaned up reserved nodes", function()
587    return format("%s nodes, %s lists of %s", cleanup(texgetcount("c_syst_last_allocated_box")))
588end) -- \topofboxstack
589
590statistics.register("node memory usage", function() -- comes after cleanup !
591    local used = usage()
592    if next(used) then
593        local t, n = { }, 0
594        for k, v in table.sortedhash(used) do
595            n = n + 1 ; t[n] = format("%s %s",v,k)
596        end
597        return table.concat(t,", ")
598    end
599end)
600
601lua.registerinitexfinalizer(cleanup, "cleanup reserved nodes")
602
603do
604
605    local glyph      = glyph
606    local traverseid = nuts.traverseid
607
608    local traversers = table.setmetatableindex(function(t,k)
609        local v = traverseid(type(k) == "number" and k or nodecodes[k],glyph)
610        t[k] = v
611        return v
612    end)
613
614    local treversers = table.setmetatableindex(function(t,k)
615        local v = traverseid(type(k) == "number" and k or nodecodes[k],glyph,true)
616        t[k] = v
617        return v
618    end)
619
620    -- these are special:
621
622    traversers.node    = nuts.traverse       (glyph)
623    traversers.char    = nuts.traversechar   (glyph)
624    traversers.glyph   = nuts.traverseglyph  (glyph)
625    traversers.list    = nuts.traverselist   (glyph)
626    traversers.content = nuts.traversecontent(glyph)
627    traversers.leader  = nuts.traverseleader (glyph)
628
629    treversers.node    = nuts.traverse       (glyph,true)
630    treversers.char    = nuts.traversechar   (glyph,true)
631    treversers.glyph   = nuts.traverseglyph  (glyph,true)
632    treversers.list    = nuts.traverselist   (glyph,true)
633    treversers.content = nuts.traversecontent(glyph,true)
634    treversers.leader  = nuts.traverseleader (glyph,true)
635
636    nuts.traversers = traversers
637    nuts.treversers = treversers
638
639end
640