node-mig.lua /size: 5958 b    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['node-mig'] = {
2    version   = 1.001,
3    comment   = "companion to node-mig.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 format = string.format
10
11local trace_migrations = false trackers.register("nodes.migrations", function(v) trace_migrations = v end)
12
13local report_nodes = logs.reporter("nodes","migrations")
14
15local attributes       = attributes
16local nodes            = nodes
17
18local nuts             = nodes.nuts
19local tonut            = nuts.tonut
20
21local getnext          = nuts.getnext
22local getid            = nuts.getid
23local getlist          = nuts.getlist
24local getprop          = nuts.getprop
25
26local setprop          = nuts.setprop
27local setlink          = nuts.setlink
28local setlist          = nuts.setlist
29local setprev          = nuts.setprev
30local setnext          = nuts.setnext
31local setboth          = nuts.setboth
32
33local remove_node      = nuts.remove
34local count            = nuts.count
35
36local nodecodes        = nodes.nodecodes
37local hlist_code       = nodecodes.hlist
38local vlist_code       = nodecodes.vlist
39local insert_code      = nodecodes.ins
40local mark_code        = nodecodes.mark
41
42local a_migrated       = attributes.private("migrated")
43local trialtypesetting = context.trialtypesetting
44
45local migrate_inserts  = false
46local migrate_marks    = false
47
48local t_inserts        = 0
49local t_marks          = 0
50local t_sweeps         = 0
51
52local function locate(head,first,last)
53    local current = head
54    while current do
55        local id = getid(current)
56        if id == vlist_code or id == hlist_code then
57            local list = getlist(current)
58            if list then
59                local l
60                l, first, last = locate(list,first,last)
61                if l ~= list then
62                    setlist(current,l)
63                end
64            end
65            current = getnext(current)
66        elseif id == insert_code then
67            if migrate_inserts then
68                local insert
69                head, current, insert = remove_node(head,current)
70                if first then
71                    setnext(insert)
72                    setlink(last,insert)
73                else
74                    setboth(insert)
75                    first = insert
76                end
77                last = insert
78            end
79        elseif id == mark_code then
80            if migrate_marks then
81                local mark
82                head, current, mark = remove_node(head,current)
83                if first then
84                    setnext(mark)
85                    setlink(last,mark)
86                else
87                    setboth(mark)
88                    first = mark
89                end
90                last = mark
91            end
92        else
93            current = getnext(current)
94        end
95    end
96    return head, first, last
97end
98
99function nodes.handlers.migrate(head,where)
100    if head and not trialtypesetting() then
101        if trace_migrations then
102            report_nodes("migration sweep %a",where)
103        end
104        local current = head
105        while current do
106            local id = getid(current)
107            if (id == vlist_code or id == hlist_code or id == insert_code) and not getprop(current,"migrated") then
108                setprop(current,"migrated",true)
109                local h = getlist(current)
110                if h then
111                    t_sweeps = t_sweeps + 1
112                    local first, last
113                    while h do
114                        local id = getid(h)
115                        if id == vlist_code or id == hlist_code then
116                            h, first, last = locate(h,first,last)
117                        end
118                        h = getnext(h)
119                    end
120                    if first then
121                        if trace_migrations then
122                            local ni = count(insert_code,first)
123                            local nm = count(mark_code,first)
124                            t_inserts = t_inserts + ni
125                            t_marks   = t_marks   + nm
126                            report_nodes("sweep %a, container %a, %s inserts and %s marks migrated outwards during %a",
127                                t_sweeps,nodecodes[id],ni,nm,where)
128                        end
129                        local n = getnext(current)
130                        if n then
131                            setlink(last,n)
132                        end
133                        setlink(current,first)
134                        current = last
135                    end
136                end
137            end
138            current = getnext(current)
139        end
140    end
141    return head
142end
143
144statistics.register("node migrations", function()
145    if trace_migrations and t_sweeps > 0 then
146        return format("%s sweeps, %s inserts moved, %s marks moved",t_sweeps,t_inserts,t_marks)
147    end
148end)
149
150-- Since we started with mkiv we had it as experiment but it is about time
151-- to have a more formal interface .. it's still optional due to possible
152-- side effects.
153
154local enableaction  = nodes.tasks.enableaction
155local disableaction = nodes.tasks.disableaction
156
157local migrations = { }
158nodes.migrations = migrations
159local enabled    = false
160
161local function check()
162    if migrate_marks or migrate_inserts then
163        if not enabled then
164            enableaction("mvlbuilders", "nodes.handlers.migrate")
165            enableaction("processors", "nodes.handlers.migrate")
166            enabled = true
167        end
168    else
169        if enabled then
170            disableaction("mvlbuilders", "nodes.handlers.migrate")
171            disableaction("processors", "nodes.handlers.migrate")
172            enabled = false
173        end
174    end
175end
176
177function migrations.setmarks(v)
178    migrate_marks = v
179    check()
180end
181
182function migrations.setinserts(v)
183    migrate_inserts = v
184    check()
185end
186