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 gmatch, format = string.gmatch, string.format
local tonumber, round = tonumber, math.round
--[[ldx--
The next function is not that much needed but in we use
for debugging node management.
--ldx]]--
local report_nodes = logs.reporter("nodes","housekeeping")
local nodes, node = nodes, node
nodes.pool = nodes.pool or { }
local nodepool = nodes.pool
local whatsitcodes = nodes.whatsitcodes
local skipcodes = nodes.skipcodes
local kerncodes = nodes.kerncodes
local rulecodes = nodes.rulecodes
local nodecodes = nodes.nodecodes
local boundarycodes = nodes.boundarycodes
local glyph_code = nodecodes.glyph
local allocate = utilities.storage.allocate
local texgetcount = tex.getcount
local reserved, nofreserved = { }, 0
-- user nodes
local userids = allocate()
local lastid = 0
setmetatable(userids, {
__index = function(t,k)
if type(k) == "string" then
lastid = lastid + 1
rawset(userids,lastid,k)
rawset(userids,k,lastid)
return lastid
else
rawset(userids,k,k)
return k
end
end,
__call = function(t,k)
return t[k]
end
} )
-- nuts overload
local nuts = nodes.nuts
local nutpool = { }
nuts.pool = nutpool
local tonut = nuts.tonut
local tonode = nuts.tonode
local getbox = nuts.getbox
local getfield = nuts.getfield
local getid = nuts.getid
local getlist = nuts.getlist
local setfield = nuts.setfield
local setchar = nuts.setchar
local setlist = nuts.setlist
local copy_nut = nuts.copy
local new_nut = nuts.new
local free_nut = nuts.free
local copy_node = nodes.copy
local new_node = nodes.new
-- 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.userids = userids
nodepool.register = register_node
nutpool.userids = userids
nutpool.register = register_node -- could be register_nut
-- so far
local disc = register_nut(new_nut("disc"))
local kern = register_nut(new_nut("kern",kerncodes.userkern))
local fontkern = register_nut(new_nut("kern",kerncodes.fontkern))
local italickern = register_nut(new_nut("kern",kerncodes.italiccorrection))
local penalty = register_nut(new_nut("penalty"))
local glue = register_nut(new_nut("glue")) -- glue.spec = nil
local glue_spec = register_nut(new_nut("glue_spec"))
local glyph = register_nut(new_nut("glyph",0))
local textdir = register_nut(new_nut("dir"))
local latelua = register_nut(new_nut("whatsit",whatsitcodes.latelua))
local special = register_nut(new_nut("whatsit",whatsitcodes.special))
local user_n = register_nut(new_nut("whatsit",whatsitcodes.userdefined)) setfield(user_n,"type",100) -- 44
local user_l = register_nut(new_nut("whatsit",whatsitcodes.userdefined)) setfield(user_l,"type",110) -- 44
local user_s = register_nut(new_nut("whatsit",whatsitcodes.userdefined)) setfield(user_s,"type",115) -- 44
local user_t = register_nut(new_nut("whatsit",whatsitcodes.userdefined)) setfield(user_t,"type",116) -- 44
----- user_c = register_nut(new_nut("whatsit",whatsitcodes.userdefined)) setfield(user_c,"type",108) -- 44
local left_margin_kern = register_nut(new_nut("margin_kern",0))
local right_margin_kern = register_nut(new_nut("margin_kern",1))
local lineskip = register_nut(new_nut("glue",skipcodes.lineskip))
local baselineskip = register_nut(new_nut("glue",skipcodes.baselineskip))
local leftskip = register_nut(new_nut("glue",skipcodes.leftskip))
local rightskip = register_nut(new_nut("glue",skipcodes.rightskip))
local temp = register_nut(new_nut("temp",0))
local noad = register_nut(new_nut("noad"))
local boundary = register_nut(new_nut("boundary",boundarycodes.user))
local wordboundary = register_nut(new_nut("boundary",boundarycodes.word))
-- the dir field needs to be set otherwise crash:
local rule = register_nut(new_nut("rule")) setfield(rule, "dir","TLT")
local emptyrule = register_nut(new_nut("rule",rulecodes.empty)) setfield(rule, "dir","TLT")
local userrule = register_nut(new_nut("rule",rulecodes.user)) setfield(rule, "dir","TLT")
local hlist = register_nut(new_nut("hlist")) setfield(hlist,"dir","TLT")
local vlist = register_nut(new_nut("vlist")) setfield(vlist,"dir","TLT")
function nutpool.zeroglue(n)
if n then
return
getfield(n,"width") == 0 and
getfield(n,"stretch") == 0 and
getfield(n,"shrink") == 0 and
getfield(n,"stretch_order") == 0 and
getfield(n,"shrink_order") == 0
else
return false
end
end
function nutpool.glyph(fnt,chr)
local n = copy_nut(glyph)
if fnt then setfield(n,"font",fnt) end
if chr then setchar(n,chr) end
return n
end
function nutpool.penalty(p)
local n = copy_nut(penalty)
if p and p ~= 0 then
setfield(n,"penalty",p)
end
return n
end
function nutpool.kern(k)
local n = copy_nut(kern)
if k and k ~= 0 then
setfield(n,"kern",k)
end
return n
end
function nutpool.boundary(v)
local n = copy_nut(boundary)
if v and v ~= 0 then
setfield(n,"value",v)
end
return n
end
function nutpool.wordboundary(v)
local n = copy_nut(wordboundary)
if v and v ~= 0 then
setfield(n,"value",v)
end
return n
end
function nutpool.fontkern(k)
local n = copy_nut(fontkern)
setfield(n,"kern",k)
return n
end
function nutpool.italickern(k)
local n = copy_nut(italickern)
if k and k ~= 0 then
setfield(n,"kern",k)
end
return n
end
function nutpool.gluespec(width,stretch,shrink,stretch_order,shrink_order)
local s = copy_nut(glue_spec)
if width and width ~= 0 then
setfield(s,"width",width)
end
if stretch and stretch ~= 0 then
setfield(s,"stretch",stretch)
end
if shrink and shrink ~= 0 then
setfield(s,"shrink",shrink)
end
if stretch_order and stretch_order ~= 0 then
setfield(s,"stretch_order",stretch_order)
end
if shrink_order and shrink_order ~= 0 then
setfield(s,"shrink_order",shrink_order)
end
return s
end
local function someskip(skip,width,stretch,shrink,stretch_order,shrink_order)
local n = copy_nut(skip)
if width and width ~= 0 then
setfield(n,"width",width)
end
if stretch and stretch ~= 0 then
setfield(n,"stretch",stretch)
end
if shrink and shrink ~= 0 then
setfield(n,"shrink",shrink)
end
if stretch_order and stretch_order ~= 0 then
setfield(n,"stretch_order",stretch_order)
end
if shrink_order and shrink_order ~= 0 then
setfield(n,"shrink_order",shrink_order)
end
return n
end
function nutpool.stretch(a,b)
local n = copy_nut(glue)
if b then
setfield(n,"stretch",a)
setfield(n,"stretch_order",b)
else
setfield(n,"stretch",1)
setfield(n,"stretch_order",a or 1)
end
return n
end
function nutpool.shrink(a,b)
local n = copy_nut(glue)
if b then
setfield(n,"shrink",a)
setfield(n,"shrink_order",b)
else
setfield(n,"shrink",1)
setfield(n,"shrink_order",a or 1)
end
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 = getfield(n,"width")
local stretch = getfield(n,"stretch")
local shrink = getfield(n,"shrink")
if width and width ~= 0 then
setfield(n,"width", -width)
end
if stretch and stretch ~= 0 then
setfield(n,"stretch",-stretch)
end
if shrink and shrink ~= 0 then
setfield(n,"shrink", -shrink)
end
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.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()
return copy_nut(disc)
end
function nutpool.textdir(dir)
local t = copy_nut(textdir)
setfield(t,"dir",dir)
return t
end
function nutpool.rule(width,height,depth,dir) -- w/h/d == nil will let them adapt
local n = copy_nut(rule)
if width then -- also 0 else adapt
setfield(n,"width",width)
end
if height then -- also 0 else adapt
setfield(n,"height",height)
end
if depth then -- also 0 else adapt
setfield(n,"depth",depth)
end
if dir then
setfield(n,"dir",dir)
end
return n
end
function nutpool.emptyrule(width,height,depth,dir) -- w/h/d == nil will let them adapt
local n = copy_nut(emptyrule)
if width then -- also 0 else adapt
setfield(n,"width",width)
end
if height then -- also 0 else adapt
setfield(n,"height",height)
end
if depth then -- also 0 else adapt
setfield(n,"depth",depth)
end
if dir then
setfield(n,"dir",dir)
end
return n
end
function nutpool.userrule(width,height,depth,dir) -- w/h/d == nil will let them adapt
local n = copy_nut(userrule)
if width then -- also 0 else adapt
setfield(n,"width",width)
end
if height then -- also 0 else adapt
setfield(n,"height",height)
end
if depth then -- also 0 else adapt
setfield(n,"depth",depth)
end
if dir then
setfield(n,"dir",dir)
end
return n
end
function nutpool.latelua(code)
local n = copy_nut(latelua)
setfield(n,"string",code)
return n
end
if context and _cldo_ then
-- a typical case where we have more nodes than nuts
local context = context
local f_cldo = string.formatters["_cldo_(%i)"]
local register = context.registerfunction
local latelua_node = register_node(new_node("whatsit",whatsitcodes.latelua))
local latelua_nut = register_nut (new_nut ("whatsit",whatsitcodes.latelua))
local setfield_node = nodes.setfield
local setfield_nut = nuts .setfield
-- function nodepool.lateluafunction(f)
-- local n = copy_node(latelua_node)
-- setfield_node(n,"string",f_cldo(register(f)))
-- return n
-- end
-- function nutpool.lateluafunction(f)
-- local n = copy_nut(latelua_nut)
-- setfield_nut(n,"string",f_cldo(register(f)))
-- return n
-- end
-- when function in latelua:
function nodepool.lateluafunction(f)
local n = copy_node(latelua_node)
setfield_node(n,"string",f)
return n
end
function nutpool.lateluafunction(f)
local n = copy_nut(latelua_nut)
setfield_nut(n,"string",f)
return n
end
local latefunction = nodepool.lateluafunction
local flushnode = context.flushnode
-- function context.lateluafunction(f)
-- flushnode(latefunction(f)) -- hm, quite some indirect calls
-- end
-- when function in latelua:
-- function context.lateluafunction(f)
-- local n = copy_node(latelua_node)
-- setfield_node(n,"string",f)
-- flushnode(n)
-- end
-- local contextsprint = context.sprint
-- local ctxcatcodes = tex.ctxcatcodes
-- local storenode = context.storenode
-- when 0.79 is out:
-- function context.lateluafunction(f)
-- contextsprint(ctxcatcodes,"\\cldl",storenode(latefunction(f))," ")
-- end
-- when function in latelua:
function context.lateluafunction(f)
local n = copy_node(latelua_node)
setfield_node(n,"string",f)
contextsprint(ctxcatcodes,"\\cldl",storenode(n)," ")
end
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
setfield(n,"width",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
setfield(n,"width",width)
end
return n
end
function nutpool.temp()
return copy_nut(temp)
end
function nutpool.noad()
return copy_nut(noad)
end
function nutpool.hlist(list,width,height,depth,shift)
local n = copy_nut(hlist)
if list then
setlist(n,list)
end
if width and width ~= 0 then
setfield(n,"width",width)
end
if height and height ~= 0 then
setfield(n,"height",height)
end
if depth and depth ~= 0 then
setfield(n,"depth",depth)
end
if shift and shift ~= 0 then
setfield(n,"shift",shift)
end
return n
end
function nutpool.vlist(list,width,height,depth,shift)
local n = copy_nut(vlist)
if list then
setlist(n,list)
end
if width and width ~= 0 then
setfield(n,"width",width)
end
if height and height ~= 0 then
setfield(n,"height",height)
end
if depth and depth ~= 0 then
setfield(n,"depth",depth)
end
if shift and shift ~= 0 then
setfield(n,"shift",shift)
end
return n
end
-- local num = userids["my id"]
-- local str = userids[num]
function nutpool.usernumber(id,num)
local n = copy_nut(user_n)
if num then
setfield(n,"user_id",id)
setfield(n,"value",num)
elseif id then
setfield(n,"value",id)
end
return n
end
function nutpool.userlist(id,list)
local n = copy_nut(user_l)
if list then
setfield(n,"user_id",id)
setfield(n,"value",list)
else
setfield(n,"value",id)
end
return n
end
function nutpool.userstring(id,str)
local n = copy_nut(user_s)
if str then
setfield(n,"user_id",id)
setfield(n,"value",str)
else
setfield(n,"value",id)
end
return n
end
function nutpool.usertokens(id,tokens)
local n = copy_nut(user_t)
if tokens then
setfield(n,"user_id",id)
setfield(n,"value",tokens)
else
setfield(n,"value",id)
end
return n
end
-- function nutpool.usercode(id,code)
-- local n = copy_nut(user_c)
-- if code then
-- setfield(n,"user_id",id)
-- setfield(n,"value",code)
-- else
-- setfield(n,"value",id)
-- end
-- return n
-- end
function nutpool.special(str)
local n = copy_nut(special)
setfield(n,"data",str)
return n
end
-- housekeeping
local function cleanup(nofboxes) -- todo
if nodes.tracers.steppers then -- to be resolved
nodes.tracers.steppers.reset() -- todo: make a registration subsystem
end
local nl, nr = 0, nofreserved
for i=1,nofreserved do
local ri = reserved[i]
-- if not (getid(ri) == glue_spec and not getfield(ri,"is_writable")) then
free_nut(reserved[i])
-- end
end
if nofboxes then
for i=0,nofboxes do
local l = getbox(i)
if l then
-- print(nodes.listtoutf(getlist(l)))
free_nut(l) -- also list ?
nl = nl + 1
end
end
end
reserved = { }
nofreserved = 0
return nr, nl, nofboxes -- can be nil
end
local function usage()
local t = { }
for n, tag in gmatch(status.node_mem_usage,"(%d+) ([a-z_]+)") do
t[tag] = n
end
return t
end
nutpool .cleanup = cleanup
nodepool.cleanup = cleanup
nutpool .usage = usage
nodepool.usage = usage
-- 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 usage = status.node_mem_usage
if usage ~= "" then
return usage
end
end)
lua.registerfinalizer(cleanup, "cleanup reserved nodes")