if not modules then modules = { } end modules ['node-nut'] = { 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" } -- This is an experimental module. Don't use nuts for generic code, at least not till -- the regular code is proven stable. No support otherwise. -- luatex: todo: copylist should return h, t -- todo: see if using insert_before and insert_after makes sense here -- This file is a side effect of the \LUATEX\ speed optimization project of Luigi -- Scarso and me. As \CONTEXT\ spends over half its time in \LUA, we though that -- using \LUAJIT\ could improve performance. We've published some of our experiences -- elsewhere, but to summarize: \LUAJITTEX\ benefits a lot from the faster virtual -- machine, but when jit is turned of we loose some again. We experimented with -- ffi (without messing up the \CONTEXT\ code too much) but there we also lost more -- than we gained (mostly due to lack of compatible userdata support: it's all or -- nothing). This made us decide to look into the \LUA||\TEX\ interfacing and by -- profiling and careful looking at the (of course then still beta source code) we -- could come up with some improvements. The first showed up in 0.75 and we've more -- on the agenda for 0.80. Although some interfaces could be sped up significantly -- in practice we're only talking of 5||10\% on a \CONTEXT\ run and maybe more when -- complex and extensive node list manipulations happens (we're talking of hundreds -- of millions cross boundary calls then for documents of hundreds pages). One of the -- changes in the \CONTEXT\ code base is that we went from indexed access to nodes to -- function calls (in principle faster weren't it that the accessors need to do more -- checking which makes them slower) and from there to optimizing these calls as well -- as providing fast variants for well defined situations. At first optimizations were -- put in a separate \type {node.fast} table although some improvements could be -- ported to the main node functions. Because we got the feeling that more gain was -- possible (especially when using more complex fonts and \MKIV\ functionality) we -- eventually abandoned this approach and dropped the \type {fast} table in favour of -- another hack. In the process we had done lots of profiling and testing so we knew -- where time was wasted, -- -- As lots of testing and experimenting was part of this project, I could not have -- done without stacks of new \CD s and \DVD s. This time Porcupine Tree, No-Man -- and Archive were came to rescue. local type, select = type, select local setmetatableindex = table.setmetatableindex -- First we get the metatable of a node: local metatable = nil do local glyph = node.new("glyph",0) metatable = getmetatable(glyph) node.free(glyph) end -- statistics.tracefunction(node, "node", "getfield","setfield") -- statistics.tracefunction(node.direct,"node.direct","getfield","setfield") -- We start with some helpers and provide all relevant basic functions in the -- node namespace as well. nodes = nodes or { } local nodes = nodes ----- gonuts = type(node.direct) == "table" -----.gonuts = gonuts local nodecodes = nodes.nodecodes local hlist_code = nodecodes.hlist local vlist_code = nodecodes.vlist nodes.tostring = node.tostring or tostring nodes.copy = node.copy nodes.copy_list = node.copy_list nodes.delete = node.delete nodes.dimensions = node.dimensions nodes.end_of_math = node.end_of_math nodes.flush_list = node.flush_list nodes.flush_node = node.flush_node nodes.free = node.free nodes.insert_after = node.insert_after nodes.insert_before = node.insert_before nodes.hpack = node.hpack nodes.new = node.new nodes.tail = node.tail nodes.traverse = node.traverse nodes.traverse_id = node.traverse_id nodes.traverse_char = node.traverse_char nodes.slide = node.slide nodes.vpack = node.vpack nodes.fields = node.fields nodes.is_node = node.is_node nodes.first_glyph = node.first_glyph nodes.has_glyph = node.has_glyph or node.first_glyph nodes.current_attr = node.current_attr nodes.do_ligature_n = node.do_ligature_n nodes.has_field = node.has_field nodes.last_node = node.last_node nodes.usedlist = node.usedlist nodes.protrusion_skippable = node.protrusion_skippable nodes.write = node.write nodes.has_attribute = node.has_attribute nodes.set_attribute = node.set_attribute nodes.unset_attribute = node.unset_attribute nodes.protect_glyphs = node.protect_glyphs nodes.protect_glyph = node.protect_glyph nodes.unprotect_glyphs = node.unprotect_glyphs nodes.kerning = node.kerning nodes.ligaturing = node.ligaturing nodes.mlist_to_hlist = node.mlist_to_hlist nodes.effective_glue = node.effective_glue nodes.is_zero_glue = node.is_zero_glue nodes.getglue = node.getglue nodes.getglue = node.getglue -- if not gonuts or not node.getfield then -- node.getfield = metatable.__index -- node.setfield = metatable.__newindex -- end nodes.tonode = function(n) return n end nodes.tonut = function(n) return n end local getfield = node.getfield local setfield = node.setfield local getattr = node.get_attribute local setattr = setfield local n_getid = node.getid local n_getlist = node.getlist local n_getnext = node.getnext local n_getprev = node.getprev local n_getchar = node.getchar local n_getfont = node.getfont local n_getsubtype = node.getsubtype local n_setfield = node.setfield local n_getfield = node.getfield local n_setattr = node.setattr local n_getattr = node.getattr local n_getdisc = node.getdisc local n_getleader = node.getleader local n_setnext = node.setnext or function(c,next) setfield(c,"next",n) end local n_setprev = node.setprev or function(c,prev) setfield(c,"prev",p) end local n_setlink = node.setlink or function(c1,c2) if c1 then setfield(c1,"next",c2) end if c2 then setfield(c2,"prev",c1) end end local n_setboth = node.setboth or function(c,p,n) setfield(c,"prev",p) setfield(c,"next",n) end node.setnext = n_setnext node.setprev = n_setprev node.setlink = n_setlink node.setboth = n_setboth nodes.getfield = n_getfield nodes.setfield = n_setfield nodes.getattr = n_getattr nodes.setattr = n_setattr nodes.getnext = n_getnext nodes.getprev = n_getprev nodes.getid = n_getid nodes.getchar = n_getchar nodes.getfont = n_getfont nodes.getsubtype = n_getsubtype nodes.getlist = n_getlist nodes.getleader = n_getleader nodes.getdisc = n_getdisc -----.getpre = node.getpre or function(n) local h, _, _, t = n_getdisc(n,true) return h, t end -----.getpost = node.getpost or function(n) local _, h, _, _, t = n_getdisc(n,true) return h, t end -----.getreplace = node.getreplace or function(n) local _, _, h, _, _, t = n_getdisc(n,true) return h, t end nodes.is_char = node.is_char nodes.ischar = node.is_char nodes.is_glyph = node.is_glyph nodes.isglyph = node.is_glyph nodes.getbox = node.getbox or tex.getbox nodes.setbox = node.setbox or tex.setbox nodes.getskip = node.getskip or tex.get local n_new_node = nodes.new local n_free_node = nodes.free local n_copy_node = nodes.copy local n_copy_list = nodes.copy_list local n_find_tail = nodes.tail local n_insert_after = nodes.insert_after local n_insert_before = nodes.insert_before local n_slide = nodes.slide local n_remove_node = node.remove -- not yet nodes.remove local function remove(head,current,free_too) local t = current head, current = n_remove_node(head,current) if not t then -- forget about it elseif free_too then n_free_node(t) t = nil else n_setboth(t) end return head, current, t end nodes.remove = remove function nodes.delete(head,current) return remove(head,current,true) end -- local h, c = nodes.replace(head,current,new) -- local c = nodes.replace(false,current,new) -- local c = nodes.replace(current,new) -- -- todo: check for new.next and find tail function nodes.replace(head,current,new) -- no head returned if false if not new then head, current, new = false, head, current -- current, new = head, current end local prev = n_getprev(current) local next = n_getnext(current) if next then n_setlink(new,next) end if prev then n_setlink(prev,new) end if head then if head == current then head = new end n_free_node(current) return head, new else n_free_node(current) return new end end local function count(stack,flat) local n = 0 while stack do local id = n_getid(stack) if not flat and id == hlist_code or id == vlist_code then local list = n_getlist(stack) if list then n = n + 1 + count(list) -- self counts too else n = n + 1 end else n = n + 1 end stack = n_getnext(stack) end return n end nodes.count = count function nodes.append(head,current,...) for i=1,select("#",...) do head, current = n_insert_after(head,current,(select(i,...))) end return head, current end function nodes.prepend(head,current,...) for i=1,select("#",...) do head, current = n_insert_before(head,current,(select(i,...))) end return head, current end function nodes.linked(...) local head, last for i=1,select("#",...) do local next = select(i,...) if next then if head then n_setlink(last,next) else head = next end last = n_find_tail(next) -- we could skip the last one end end return head end function nodes.concat(list) -- consider tail instead of slide local head, tail for i=1,#list do local li = list[i] if li then if head then n_setlink(tail,li) else head = li end tail = n_slide(li) end end return head, tail end --[[
At some point we ran into a problem that the glue specification of the zeropoint dimension was overwritten when adapting a glue spec node. This is a side effect of glue specs being shared. After a couple of hours tracing and debugging Taco and I came to the conclusion that it made no sense to complicate the spec allocator and settled on a writable flag. This all is a side effect of the fact that some glues use reserved memory slots (with the zeropoint glue being a noticeable one). So, next we wrap this into a function and hide it for the user. And yes, LuaTeX now gives a warning as well.
]]-- function nodes.reference(n) return n and tonut(n) or "