if not modules then modules = { } end modules ['node-res'] = { version = 1.001, comment = "companion to node-ini.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } local type, next, rawset = type, next, rawset local gmatch, format = string.gmatch, string.format local round = math.round local nodes, node = nodes, node local report_nodes = logs.reporter("nodes","housekeeping") nodes.pool = nodes.pool or { } local nodepool = nodes.pool local gluecodes = nodes.gluecodes local kerncodes = nodes.kerncodes local rulecodes = nodes.rulecodes local nodecodes = nodes.nodecodes local boundarycodes = nodes.boundarycodes local usercodes = nodes.usercodes local nodeproperties = nodes.properties.data local glyph_code = nodecodes.glyph local rule_code = nodecodes.rule local kern_code = nodecodes.kern local glue_code = nodecodes.glue local gluespec_code = nodecodes.gluespec local currentfont = font.current local texgetcount = tex.getcount local allocate = utilities.storage.allocate local reserved = { } local nofreserved = 0 -- nuts overload local nuts = nodes.nuts local nutpool = { } nuts.pool = nutpool local tonut = nuts.tonut local tonode = nuts.tonode local getbox = nuts.getbox local getid = nuts.getid local getlist = nuts.getlist local getglue = nuts.getglue local setfield = nuts.setfield local setchar = nuts.setchar local setlist = nuts.setlist local setwhd = nuts.setwhd local setglue = nuts.setglue local setdisc = nuts.setdisc local setfont = nuts.setfont local setkern = nuts.setkern local setpenalty = nuts.setpenalty local setdir = nuts.setdir local setdirection = nuts.setdirection local setshift = nuts.setshift local setwidth = nuts.setwidth local setsubtype = nuts.setsubtype local setleader = nuts.setleader local setclass = nuts.setclass local setdata = nuts.setdata local setoffsets = nuts.setoffsets local setvalue = nuts.setvalue local setruledimensions = nuts.setruledimensions local copy_nut = nuts.copyonly local new_nut = nuts.new local flush_nut = nuts.flush -- at some point we could have a dual set (the overhead of tonut is not much larger than -- metatable associations at the lua/c end esp if we also take assignments into account -- table.setmetatableindex(nodepool,function(t,k,v) -- -- report_nodes("defining nodepool[%s] instance",k) -- local f = nutpool[k] -- local v = function(...) -- return tonode(f(...)) -- end -- t[k] = v -- return v -- end) -- -- -- we delay one step because that permits us a forward reference -- -- e.g. in pdfsetmatrix table.setmetatableindex(nodepool,function(t,k,v) -- report_nodes("defining nodepool[%s] instance",k) local v = function(...) local f = nutpool[k] local v = function(...) return tonode(f(...)) end t[k] = v return v(...) end t[k] = v return v end) local function register_nut(n) nofreserved = nofreserved + 1 reserved[nofreserved] = n return n end local function register_node(n) nofreserved = nofreserved + 1 if type(n) == "number" then -- isnut(n) reserved[nofreserved] = n else reserved[nofreserved] = tonut(n) end return n end nodepool.register = register_node nutpool.register = register_node -- could be register_nut -- so far local disc = register_nut(new_nut(nodecodes.disc)) local kern = register_nut(new_nut(kern_code,kerncodes.userkern)) local fontkern = register_nut(new_nut(kern_code,kerncodes.fontkern)) local italickern = register_nut(new_nut(kern_code,kerncodes.italiccorrection)) local spacefontkern = register_nut(new_nut(kern_code,kerncodes.spacefontkern)) local penalty = register_nut(new_nut(nodecodes.penalty)) local glue = register_nut(new_nut(glue_code)) local gluespec = register_nut(new_nut(gluespec_code)) local glyph = register_nut(new_nut(glyph_code,0)) local textdir = register_nut(new_nut(nodecodes.dir)) local left_margin_kern = register_nut(new_nut(kern_code,kerncodes.leftmargincode)) local right_margin_kern = register_nut(new_nut(kern_code,kerncodes.rightmargincode)) local lineskip = register_nut(new_nut(glue_code,gluecodes.lineskip)) local baselineskip = register_nut(new_nut(glue_code,gluecodes.baselineskip)) local leftskip = register_nut(new_nut(glue_code,gluecodes.leftskip)) local rightskip = register_nut(new_nut(glue_code,gluecodes.rightskip)) local lefthangskip = register_nut(new_nut(glue_code,gluecodes.lefthangskip)) local righthangskip = register_nut(new_nut(glue_code,gluecodes.righthangskip)) local indentskip = register_nut(new_nut(glue_code,gluecodes.indentskip)) local correctionskip = register_nut(new_nut(glue_code,gluecodes.correctionskip)) local temp = register_nut(new_nut(nodecodes.temp,0)) local noad = register_nut(new_nut(nodecodes.noad)) local delimiter = register_nut(new_nut(nodecodes.delimiter)) local fence = register_nut(new_nut(nodecodes.fence)) local submlist = register_nut(new_nut(nodecodes.submlist)) local accent = register_nut(new_nut(nodecodes.accent)) local radical = register_nut(new_nut(nodecodes.radical)) local fraction = register_nut(new_nut(nodecodes.fraction)) local subbox = register_nut(new_nut(nodecodes.subbox)) local mathchar = register_nut(new_nut(nodecodes.mathchar)) local mathtextchar = register_nut(new_nut(nodecodes.mathtextchar)) local choice = register_nut(new_nut(nodecodes.choice)) local boundary = register_nut(new_nut(nodecodes.boundary,boundarycodes.user)) local wordboundary = register_nut(new_nut(nodecodes.boundary,boundarycodes.word)) local cleader = register_nut(copy_nut(glue)) setsubtype(cleader,gluecodes.cleaders) setglue(cleader,0,65536,0,2,0) -- the dir field needs to be set otherwise crash: local lefttoright_code = tex.directioncodes.lefttoright local rule = register_nut(new_nut(rule_code)) -- setdirection(rule, lefttoright_code) local emptyrule = register_nut(new_nut(rule_code,rulecodes.empty)) -- setdirection(rule, lefttoright_code) local strutrule = register_nut(new_nut(rule_code,rulecodes.strut)) -- setdirection(rule, lefttoright_code) local userrule = register_nut(new_nut(rule_code,rulecodes.user)) -- setdirection(rule, lefttoright_code) local outlinerule = register_nut(new_nut(rule_code,rulecodes.outline)) -- setdirection(rule, lefttoright_code) local imagerule = register_nut(new_nut(rule_code,rulecodes.image)) -- setdirection(rule, lefttoright_code) local boxrule = register_nut(new_nut(rule_code,rulecodes.box)) -- setdirection(rule, lefttoright_code) local virtualrule = register_nut(new_nut(rule_code,rulecodes.virtual)) -- setdirection(rule, lefttoright_code) local hlist = register_nut(new_nut(nodecodes.hlist)) setdirection(hlist,lefttoright_code) local vlist = register_nut(new_nut(nodecodes.vlist)) setdirection(vlist,lefttoright_code) function nutpool.glyph(fnt,chr) local n = copy_nut(glyph) if fnt then setfont(n,fnt == true and currentfont() or fnt,chr) elseif chr then setchar(n,chr) end return n end function nutpool.penalty(p) local n = copy_nut(penalty) if p and p ~= 0 then setpenalty(n,p) end return n end function nutpool.kern(k) local n = copy_nut(kern) if k and k ~= 0 then setkern(n,k) end return n end function nutpool.boundary(v) local n = copy_nut(boundary) if v and v ~= 0 then setvalue(n,v) end return n end function nutpool.wordboundary(v) local n = copy_nut(wordboundary) if v and v ~= 0 then setvalue(n,v) end return n end function nutpool.fontkern(k) local n = copy_nut(fontkern) if k and k ~= 0 then setkern(n,k) end return n end function nutpool.italickern(k) local n = copy_nut(italickern) if k and k ~= 0 then setkern(n,k) end return n end function nutpool.spacefontkern(k) local n = copy_nut(spacefontkern) if k and k ~= 0 then setkern(n,k) end return n end function nutpool.gluespec(width,stretch,shrink,stretch_order,shrink_order) local n = copy_nut(gluespec) if width or stretch or shrink or stretch_order or shrink_order then setglue(n,width,stretch,shrink,stretch_order,shrink_order) end return n end local function someskip(skip,width,stretch,shrink,stretch_order,shrink_order) -- maybe setglue local n = copy_nut(skip) if width or stretch or shrink or stretch_order or shrink_order then setglue(n,width,stretch,shrink,stretch_order,shrink_order) end return n end function nutpool.stretch(a,b) -- width stretch shrink stretch_order shrink_order local n = copy_nut(glue) if not b then a, b = 1, a or 1 end setglue(n,0,a,0,b,0) return n end function nutpool.shrink(a,b) local n = copy_nut(glue) if not b then a, b = 1, a or 1 end setglue(n,0,0,a,0,0,b) return n end function nutpool.glue(width,stretch,shrink,stretch_order,shrink_order) return someskip(glue,width,stretch,shrink,stretch_order,shrink_order) end function nutpool.negatedglue(glue) local n = copy_nut(glue) local width, stretch, shrink = getglue(n) setglue(n,-width,-stretch,-shrink) return n end function nutpool.leftskip(width,stretch,shrink,stretch_order,shrink_order) return someskip(leftskip,width,stretch,shrink,stretch_order,shrink_order) end function nutpool.rightskip(width,stretch,shrink,stretch_order,shrink_order) return someskip(rightskip,width,stretch,shrink,stretch_order,shrink_order) end function nutpool.lefthangskip(width,stretch,shrink,stretch_order,shrink_order) return someskip(lefthangskip,width,stretch,shrink,stretch_order,shrink_order) end function nutpool.righthangskip(width,stretch,shrink,stretch_order,shrink_order) return someskip(righthangskip,width,stretch,shrink,stretch_order,shrink_order) end function nutpool.indentskip(width,stretch,shrink,stretch_order,shrink_order) return someskip(indentskip,width,stretch,shrink,stretch_order,shrink_order) end function nutpool.lineskip(width,stretch,shrink,stretch_order,shrink_order) return someskip(lineskip,width,stretch,shrink,stretch_order,shrink_order) end function nutpool.baselineskip(width,stretch,shrink) return someskip(baselineskip,width,stretch,shrink) end function nutpool.disc(pre,post,replace) local d = copy_nut(disc) if pre or post or replace then setdisc(d,pre,post,replace) end return d end function nutpool.direction(dir,swap) local t = copy_nut(textdir) if not dir then -- just a l2r start node elseif swap then setdirection(t,dir,true) else setdirection(t,dir,false) end return t end function nutpool.rule(width,height,depth) -- w/h/d == nil will let them adapt local n = copy_nut(rule) if width or height or depth then setwhd(n,width,height,depth) end return n end function nutpool.emptyrule(width,height,depth) -- w/h/d == nil will let them adapt local n = copy_nut(emptyrule) if width or height or depth then setwhd(n,width,height,depth) end return n end -- data left right function nutpool.strutrule(width,height,depth) -- w/h/d == nil will let them adapt local n = copy_nut(strutrule) if width or height or depth then setwhd(n,width,height,depth) end return n end function nutpool.userrule(width,height,depth) -- w/h/d == nil will let them adapt local n = copy_nut(userrule) if width or height or depth then setwhd(n,width,height,depth) end return n end function nutpool.outlinerule(width,height,depth,line) -- w/h/d == nil will let them adapt local n = copy_nut(outlinerule) if width or height or depth then setwhd(n,width,height,depth) end if line then setdata(n,round(line)) -- has to be an integer end return n end function nutpool.imagerule(width,height,depth) -- w/h/d == nil will let them adapt local n = copy_nut(imagerule) if width or height or depth then setwhd(n,width,height,depth) end return n end function nutpool.boxrule(width,height,depth) -- w/h/d == nil will let them adapt local n = copy_nut(boxrule) if width or height or depth then setwhd(n,width,height,depth) end return n end function nutpool.virtualrule(width,height,depth,data) local n = copy_nut(virtualrule) if width or height or depth or data then setruledimensions(n,width,height,depth,data) end return n end local function new_leader(width,list) local n = copy_nut(cleader) if width then setwidth(n,width) end if list then setleader(n,list) end return n end nutpool.leader = new_leader function nodepool.leader(width,list) return tonode(new_leader(width,list and tonut(list))) end function nutpool.leftmarginkern(glyph,width) local n = copy_nut(left_margin_kern) if not glyph then report_nodes("invalid pointer to left margin glyph node") elseif getid(glyph) ~= glyph_code then report_nodes("invalid node type %a for %s margin glyph node",nodecodes[glyph],"left") else setfield(n,"glyph",glyph) end if width and width ~= 0 then setwidth(n,width) end return n end function nutpool.rightmarginkern(glyph,width) local n = copy_nut(right_margin_kern) if not glyph then report_nodes("invalid pointer to right margin glyph node") elseif getid(glyph) ~= glyph_code then report_nodes("invalid node type %a for %s margin glyph node",nodecodes[p],"right") else setfield(n,"glyph",glyph) end if width and width ~= 0 then setwidth(n,width) end return n end function nutpool.temp() return copy_nut(temp) end function nutpool.noad(class) local n = copy_nut(noad) if class then setsubtype(n,class) setclass(n,class,class,class) end return n end -- maybe also the rest wrt subtype and class function nutpool.delimiter() return copy_nut(delimiter) end nutpool.delim = nutpool.delimiter function nutpool.fence() return copy_nut(fence) end function nutpool.submlist() return copy_nut(submlist) end function nutpool.fence() return copy_nut(fence) end function nutpool.accent() return copy_nut(accent) end function nutpool.radical() return copy_nut(radical) end function nutpool.fraction() return copy_nut(fraction) end function nutpool.subbox() return copy_nut(subbox) end function nutpool.mathchar() return copy_nut(mathchar) end function nutpool.mathtextchar() return copy_nut(mathtextchar) end function nutpool.choice() return copy_nut(choice) end local function new_hlist(list,width,height,depth,shift) local n = copy_nut(hlist) if list then setlist(n,list) end if width or height or depth then setwhd(n,width,height,depth) end if shift and shift ~= 0 then setshift(n,shift) end return n end local function new_vlist(list,width,height,depth,shift) local n = copy_nut(vlist) if list then setlist(n,list) end if width or height or depth then setwhd(n,width,height,depth) end if shift and shift ~= 0 then setshift(n,shift) end return n end nutpool.hlist = new_hlist nutpool.vlist = new_vlist function nodepool.hlist(list,width,height,depth,shift) return tonode(new_hlist(list and tonut(list),width,height,depth,shift)) end function nodepool.vlist(list,width,height,depth,shift) return tonode(new_vlist(list and tonut(list),width,height,depth,shift)) end function nutpool.usernode(id,data) local n = copy_nut(user_node) nodeproperties[n] = { id = id, data = data, } return n end -- housekeeping local function cleanup(nofboxes) -- todo -- this is bonus, not really needed local tracers = nodes.tracers if tracers and tracers.steppers then -- to be resolved tracers.steppers.reset() -- todo: make a registration subsystem end local nl = 0 local nr = nofreserved for i=1,nofreserved do local ri = reserved[i] flush_nut(reserved[i]) end if nofboxes then for i=0,nofboxes do local l = getbox(i) if l then flush_nut(l) -- also list ? nl = nl + 1 end end end reserved = { } nofreserved = 0 return nr, nl, nofboxes -- can be nil end local usage = node.inuse local stock = node.instock nutpool .cleanup = cleanup nodepool.cleanup = cleanup nutpool .usage = usage nodepool.usage = usage nutpool .stock = stock nodepool.stock = stock -- end statistics.register("cleaned up reserved nodes", function() return format("%s nodes, %s lists of %s", cleanup(texgetcount("c_syst_last_allocated_box"))) end) -- \topofboxstack statistics.register("node memory usage", function() -- comes after cleanup ! local used = usage() if next(used) then local t, n = { }, 0 for k, v in table.sortedhash(used) do n = n + 1 ; t[n] = format("%s %s",v,k) end return table.concat(t,", ") end end) lua.registerinitexfinalizer(cleanup, "cleanup reserved nodes") do local glyph = glyph local traverseid = nuts.traverseid local traversers = table.setmetatableindex(function(t,k) local v = traverseid(type(k) == "number" and k or nodecodes[k],glyph) t[k] = v return v end) local treversers = table.setmetatableindex(function(t,k) local v = traverseid(type(k) == "number" and k or nodecodes[k],glyph,true) t[k] = v return v end) -- these are special: traversers.node = nuts.traverse (glyph) traversers.char = nuts.traversechar (glyph) traversers.glyph = nuts.traverseglyph (glyph) traversers.list = nuts.traverselist (glyph) traversers.content = nuts.traversecontent(glyph) traversers.leader = nuts.traverseleader (glyph) treversers.node = nuts.traverse (glyph,true) treversers.char = nuts.traversechar (glyph,true) treversers.glyph = nuts.traverseglyph (glyph,true) treversers.list = nuts.traverselist (glyph,true) treversers.content = nuts.traversecontent(glyph,true) treversers.leader = nuts.traverseleader (glyph,true) nuts.traversers = traversers nuts.treversers = treversers end