3 % Copyright
2006-2010 Taco Hoekwater
<taco@@luatex.org
>
5 % This file is part of LuaTeX.
7 % LuaTeX is free software
; you can redistribute it and
/or modify it under
8 % the terms of the GNU General Public License as published by the Free
9 % Software Foundation
; either version
2 of the License
, or
(at your
10 % option
) any later version.
12 % LuaTeX is distributed in the hope that it will be useful
, but WITHOUT
13 % ANY WARRANTY
; without even the implied warranty of MERCHANTABILITY or
14 % FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 % License for more details.
17 % You should have received a copy of the GNU General Public License along
18 % with LuaTeX
; if not
, see
<http
://www.gnu.org
/licenses
/>.
23 #include
"lua/luatex-api.h"
25 /* we can consider less mode sizes
: 2 4 6 8 */
28 This module started out using NDEBUG to trigger checking invalid node usage
,
29 something that is needed because users can mess up nodes in Lua. At some point
30 that code was always enabled so it is now always on but still can be recognized
31 as additional code. And as the performance hit is close to zero so disabling
32 makes no sense
, not even to make it configureable. There is a little more memory
33 used but that is neglectable compared to other memory usage.
36 #define attribute
(A
) eqtb
[attribute_base
+(A
)].cint
38 #define uc_hyph int_par
(uc_hyph_code
)
39 #define cur_lang int_par
(cur_lang_code
)
40 #define left_hyphen_min int_par
(left_hyphen_min_code
)
41 #define right_hyphen_min int_par
(right_hyphen_min_code
)
43 #define MAX_CHAIN_SIZE
13 /* why not a bit larger
*/
45 #define CHECK_NODE_USAGE
1 /* this triggers checking
*/
47 memory_word
*volatile varmem
= NULL;
49 #ifdef CHECK_NODE_USAGE
50 char
*varmem_sizes
= NULL;
53 halfword var_mem_max
= 0;
56 halfword free_chain
[MAX_CHAIN_SIZE
] = { null
};
58 static int my_prealloc
= 0;
60 int fix_node_lists
= 1; /* used in font and lang
*/
62 halfword slow_get_node
(int s
); /* defined below
*/
65 #define fake_node_size
2
66 #define fake_node_name
"fake"
68 #define variable_node_size
2
72 const char
*node_fields_list
[] = {
73 "attr", "width", "depth", "height", "dir", "shift", "glue_order", "glue_sign",
74 "glue_set", "head", NULL
76 const char
*node_fields_rule
[] = {
77 "attr", "width", "depth", "height", "dir", "index", NULL
79 const char
*node_fields_insert
[] = {
80 "attr", "cost", "depth", "height", "spec", "head", NULL
82 const char
*node_fields_mark
[] = {
83 "attr", "class", "mark", NULL
85 const char
*node_fields_adjust
[] = {
88 const char
*node_fields_disc
[] = {
89 "attr", "pre", "post", "replace", "penalty", NULL
91 const char
*node_fields_math
[] = {
92 "attr", "surround", NULL
94 const char
*node_fields_glue
[] = {
95 "attr", "spec", "leader", NULL
97 const char
*node_fields_kern
[] = {
98 "attr", "kern", "expansion_factor", NULL
100 const char
*node_fields_penalty
[] = {
101 "attr", "penalty", NULL
103 const char
*node_fields_unset
[] = {
104 "attr", "width", "depth", "height", "dir", "shrink", "glue_order",
105 "glue_sign", "stretch", "span", "head", NULL
107 const char
*node_fields_margin_kern
[] = {
108 "attr", "width", "glyph", NULL
110 const char
*node_fields_glyph
[] = {
111 "attr", "char", "font", "lang", "left", "right", "uchyph", "components",
112 "xoffset", "yoffset", "width", "height", "depth", "expansion_factor", NULL
114 const char
*node_fields_inserting
[] = {
115 "height", "last_ins_ptr", "best_ins_ptr", NULL
117 const char
*node_fields_splitup
[] = {
118 "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins", NULL
120 const char
*node_fields_attribute
[] = {
121 "number", "value", NULL
123 const char
*node_fields_glue_spec
[] = {
124 "width", "stretch", "shrink", "stretch_order", "shrink_order", "ref_count",
127 const char
*node_fields_attribute_list
[] = {
130 const char
*node_fields_local_par
[] = {
131 "attr", "pen_inter", "pen_broken", "dir", "box_left", "box_left_width",
132 "box_right", "box_right_width", NULL
134 const char
*node_fields_dir
[] = {
135 "attr", "dir", "level", "dvi_ptr", "dvi_h", NULL
137 const char
*node_fields_boundary
[] = {
138 "attr", "value", NULL
143 const char
*node_fields_noad
[] = {
144 "attr", "nucleus", "sub", "sup", NULL
147 #define node_fields_ord node_fields_noad
148 #define node_fields_op node_fields_noad
149 #define node_fields_bin node_fields_noad
150 #define node_fields_rel node_fields_noad
151 #define node_fields_open node_fields_noad
152 #define node_fields_close node_fields_noad
153 #define node_fields_punct node_fields_noad
154 #define node_fields_inner node_fields_noad
155 #define node_fields_under node_fields_noad
156 #define node_fields_over node_fields_noad
157 #define node_fields_vcenter node_fields_noad
159 const char
*node_fields_style
[] = {
160 "attr", "style", NULL
162 const char
*node_fields_choice
[] = {
163 "attr", "display", "text", "script", "scriptscript", NULL
165 const char
*node_fields_radical
[] = {
166 "attr", "nucleus", "sub", "sup", "left", "degree", NULL
168 const char
*node_fields_fraction
[] = {
169 "attr", "width", "num", "denom", "left", "right", NULL
171 const char
*node_fields_accent
[] = {
172 "attr", "nucleus", "sub", "sup", "accent", "bot_accent", "top_accent",
173 "overlay_accent", NULL
175 const char
*node_fields_fence
[] = {
176 "attr", "delim", NULL
178 const char
*node_fields_math_char
[] = {
179 "attr", "fam", "char", NULL
181 const char
*node_fields_sub_box
[] = {
184 const char
*node_fields_sub_mlist
[] = {
187 const char
*node_fields_math_text_char
[] = {
188 "attr", "fam", "char", NULL
190 const char
*node_fields_delim
[] = {
191 "attr", "small_fam", "small_char", "large_fam", "large_char", NULL
196 const char
*node_fields_whatsit_open
[] = {
197 "attr", "stream", "name", "area", "ext", NULL
199 const char
*node_fields_whatsit_write
[] = {
200 "attr", "stream", "data", NULL
202 const char
*node_fields_whatsit_close
[] = {
203 "attr", "stream", NULL
205 const char
*node_fields_whatsit_special
[] = {
208 const char
*node_fields_whatsit_save_pos
[] = {
211 const char
*node_fields_whatsit_late_lua
[] = {
212 "attr", "reg", "data", "name", "string", NULL
214 const char
*node_fields_whatsit_user_defined
[] = {
215 "attr", "user_id", "type", "value", NULL
218 /* pdf backend whatsit nodes
*/
220 const char
*node_fields_whatsit_pdf_literal
[] = {
221 "attr", "mode", "data", NULL
223 const char
*node_fields_whatsit_pdf_refobj
[] = {
224 "attr", "objnum", NULL
226 const char
*node_fields_whatsit_pdf_annot
[] = {
227 "attr", "width", "depth", "height", "objnum", "data", NULL
229 const char
*node_fields_whatsit_pdf_start_link
[] = {
230 "attr", "width", "depth", "height", "objnum", "link_attr", "action", NULL
232 const char
*node_fields_whatsit_pdf_end_link
[] = {
235 const char
*node_fields_whatsit_pdf_dest
[] = {
236 "attr", "width", "depth", "height", "named_id", "dest_id", "dest_type",
237 "xyz_zoom", "objnum", NULL
239 const char
*node_fields_whatsit_pdf_action
[] = {
240 "action_type", "named_id", "action_id", "file", "new_window", "data",
243 const char
*node_fields_whatsit_pdf_thread
[] = {
244 "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL
246 const char
*node_fields_whatsit_pdf_start_thread
[] = {
247 "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL
249 const char
*node_fields_whatsit_pdf_end_thread
[] = {
252 const char
*node_fields_whatsit_pdf_colorstack
[] = {
253 "attr", "stack", "cmd", "data", NULL
255 const char
*node_fields_whatsit_pdf_setmatrix
[] = {
258 const char
*node_fields_whatsit_pdf_save
[] = {
261 const char
*node_fields_whatsit_pdf_restore
[] = {
267 const char
*node_subtypes_glue
[] = {
268 "userskip", "lineskip", "baselineskip", "parskip", "abovedisplayskip", "belowdisplayskip",
269 "abovedisplayshortskip", "belowdisplayshortskip", "leftskip", "rightskip", "topskip",
270 "splittopskip", "tabskip", "spaceskip", "xspaceskip", "parfillskip", "thinmuskip",
271 "medmuskip", "thickmuskip", "mathskip", NULL
273 const char
*node_subtypes_leader
[] = { /* 100+ */
274 "leaders", "cleaders", "xleaders", "gleaders", NULL
276 const char
*node_subtypes_fill
[] = {
277 "stretch", "fi", "fil", "fill", "filll", NULL
279 const char
*node_subtypes_penalty
[] = {
282 const char
*node_subtypes_kern
[] = {
283 "fontkern", "userkern", "accentkern", "italiccorrection", NULL
285 const char
*node_subtypes_rule
[] = {
286 "normal", "box", "image", "empty", "user", NULL
288 const char
*node_subtypes_glyph
[] = {
289 "character", "glyph", "ligature", "ghost", "left", "right", NULL
291 const char
*node_subtypes_disc
[] = {
292 "discretionary", "explicit", "automatic", "regular", "first", "second", NULL
294 const char
*node_subtypes_marginkern
[] = {
295 "left", "right", NULL
297 const char
*node_subtypes_list
[] = {
298 "unknown", "line", "box", "indent", "alignment", "cell", "equation", "equationnumber", NULL
300 const char
*node_subtypes_math
[] = {
301 "beginmath", "endmath", NULL
303 const char
*node_subtypes_noad
[] = {
304 "ord", "opdisplaylimits", "oplimits", "opnolimits", "bin", "rel", "open", "close",
305 "punct", "inner", "under", "over", "vcenter", NULL
307 const char
*node_subtypes_radical
[] = {
308 "radical", "uradical", "uroot", "uunderdelimiter", "uoverdelimiter", "udelimiterunder",
309 "udelimiterover", NULL
311 const char
*node_subtypes_accent
[] = {
312 "bothflexible", "fixedtop", "fixedbottom", "fixedboth", NULL,
314 const char
*node_subtypes_fence
[] = {
315 "unset", "left", "middle", "right", NULL
318 node_info node_data
[] = { /* the last entry in a row is the etex number
*/
319 { hlist_node
, box_node_size
, node_fields_list
, "hlist", 1 },
320 { vlist_node
, box_node_size
, node_fields_list
, "vlist", 2 },
321 { rule_node
, rule_node_size
, node_fields_rule
, "rule", 3 },
322 { ins_node
, ins_node_size
, node_fields_insert
, "ins", 4 },
323 { mark_node
, mark_node_size
, node_fields_mark
, "mark", 5 },
324 { adjust_node
, adjust_node_size
, node_fields_adjust
, "adjust", 6 },
325 { boundary_node
, boundary_size
, node_fields_boundary
, "boundary", -1 },
326 { disc_node
, disc_node_size
, node_fields_disc
, "disc", 8 },
327 { whatsit_node
, -1, NULL, "whatsit", 9 },
328 { local_par_node
, local_par_size
, node_fields_local_par
, "local_par", -1 },
329 { dir_node
, dir_node_size
, node_fields_dir
, "dir", -1 },
330 { math_node
, math_node_size
, node_fields_math
, "math", 10 },
331 { glue_node
, glue_node_size
, node_fields_glue
, "glue", 11 },
332 { kern_node
, kern_node_size
, node_fields_kern
, "kern", 12 },
333 { penalty_node
, penalty_node_size
, node_fields_penalty
, "penalty", 13 },
334 { unset_node
, box_node_size
, node_fields_unset
, "unset", 14 },
335 { style_node
, style_node_size
, node_fields_style
, "style", 15 },
336 { choice_node
, style_node_size
, node_fields_choice
, "choice", 15 },
337 { simple_noad
, noad_size
, node_fields_ord
, "noad", 15 },
338 { radical_noad
, radical_noad_size
, node_fields_radical
, "radical", 15 },
339 { fraction_noad
, fraction_noad_size
, node_fields_fraction
, "fraction", 15 },
340 { accent_noad
, accent_noad_size
, node_fields_accent
, "accent", 15 },
341 { fence_noad
, fence_noad_size
, node_fields_fence
, "fence", 15 },
342 { math_char_node
, math_kernel_node_size
, node_fields_math_char
, "math_char", 15 },
343 { sub_box_node
, math_kernel_node_size
, node_fields_sub_box
, "sub_box", 15 },
344 { sub_mlist_node
, math_kernel_node_size
, node_fields_sub_mlist
, "sub_mlist", 15 },
345 { math_text_char_node
, math_kernel_node_size
, node_fields_math_text_char
, "math_text_char", 15 },
346 { delim_node
, math_shield_node_size
, node_fields_delim
, "delim", 15 },
347 { margin_kern_node
, margin_kern_node_size
, node_fields_margin_kern
, "margin_kern", -1 },
348 { glyph_node
, glyph_node_size
, node_fields_glyph
, "glyph", 0 },
349 { align_record_node
, box_node_size
, NULL, "align_record", -1 },
350 { pseudo_file_node
, pseudo_file_node_size
, NULL, "pseudo_file", -1 },
351 { pseudo_line_node
, variable_node_size
, NULL, "pseudo_line", -1 },
352 { inserting_node
, page_ins_node_size
, node_fields_inserting
, "page_insert", -1 },
353 { split_up_node
, page_ins_node_size
, node_fields_splitup
, "split_insert", -1 },
354 { expr_node
, expr_node_size
, NULL, "expr_stack", -1 },
355 { nesting_node
, nesting_node_size
, NULL, "nested_list", -1 },
356 { span_node
, span_node_size
, NULL, "span", -1 },
357 { attribute_node
, attribute_node_size
, node_fields_attribute
, "attribute", -1 },
358 { glue_spec_node
, glue_spec_size
, node_fields_glue_spec
, "glue_spec", -1 },
359 { attribute_list_node
, attribute_node_size
, node_fields_attribute_list
, "attribute_list", -1 },
360 { temp_node
, temp_node_size
, NULL, "temp", -1 },
361 { align_stack_node
, align_stack_node_size
, NULL, "align_stack", -1 },
362 { movement_node
, movement_node_size
, NULL, "movement_stack", -1 },
363 { if_node
, if_node_size
, NULL, "if_stack", -1 },
364 { unhyphenated_node
, active_node_size
, NULL, "unhyphenated", -1 },
365 { hyphenated_node
, active_node_size
, NULL, "hyphenated", -1 },
366 { delta_node
, delta_node_size
, NULL, "delta", -1 },
367 { passive_node
, passive_node_size
, NULL, "passive", -1 },
368 { shape_node
, variable_node_size
, NULL, "shape", -1 },
369 { -1, -1, NULL, NULL, -1 },
372 #define last_normal_node shape_node
374 const char
*node_subtypes_pdf_destination
[] = {
375 "xyz", "fit", "fith", "fitv", "fitb", "fitbh", "fitbv", "fitr", NULL
377 const char
*node_subtypes_pdf_literal
[] = {
378 "origin", "page", "direct", NULL
381 node_info whatsit_node_data
[] = {
382 { open_node
, open_node_size
, node_fields_whatsit_open
, "open", -1 },
383 { write_node
, write_node_size
, node_fields_whatsit_write
, "write", -1 },
384 { close_node
, close_node_size
, node_fields_whatsit_close
, "close", -1 },
385 { special_node
, special_node_size
, node_fields_whatsit_special
, "special", -1 },
386 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
387 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
388 { save_pos_node
, save_pos_node_size
, node_fields_whatsit_save_pos
, "save_pos", -1 },
389 { late_lua_node
, late_lua_node_size
, node_fields_whatsit_late_lua
, "late_lua", -1 },
390 { user_defined_node
, user_defined_node_size
, node_fields_whatsit_user_defined
, "user_defined", -1 },
391 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
392 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
393 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
394 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
395 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
396 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
397 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
398 /* here starts the dvi backend section
, todo
: a separate list
*/
399 /* nothing for dvi
*/
400 /* here starts the pdf backend section
, todo
: a separate list
*/
401 { pdf_literal_node
, write_node_size
, node_fields_whatsit_pdf_literal
, "pdf_literal", -1 },
402 { pdf_refobj_node
, pdf_refobj_node_size
, node_fields_whatsit_pdf_refobj
, "pdf_refobj", -1 },
403 { pdf_annot_node
, pdf_annot_node_size
, node_fields_whatsit_pdf_annot
, "pdf_annot", -1 },
404 { pdf_start_link_node
, pdf_annot_node_size
, node_fields_whatsit_pdf_start_link
, "pdf_start_link", -1 },
405 { pdf_end_link_node
, pdf_end_link_node_size
, node_fields_whatsit_pdf_end_link
, "pdf_end_link", -1 },
406 { pdf_dest_node
, pdf_dest_node_size
, node_fields_whatsit_pdf_dest
, "pdf_dest", -1 },
407 { pdf_action_node
, pdf_action_size
, node_fields_whatsit_pdf_action
, "pdf_action", -1 },
408 { pdf_thread_node
, pdf_thread_node_size
, node_fields_whatsit_pdf_thread
, "pdf_thread", -1 },
409 { pdf_start_thread_node
, pdf_thread_node_size
, node_fields_whatsit_pdf_start_thread
, "pdf_start_thread", -1 },
410 { pdf_end_thread_node
, pdf_end_thread_node_size
, node_fields_whatsit_pdf_end_thread
, "pdf_end_thread", -1 },
411 { pdf_thread_data_node
, pdf_thread_node_size
, NULL, "pdf_thread_data", -1 },
412 { pdf_link_data_node
, pdf_annot_node_size
, NULL, "pdf_link_data", -1 },
413 { pdf_colorstack_node
, pdf_colorstack_node_size
, node_fields_whatsit_pdf_colorstack
, "pdf_colorstack", -1 },
414 { pdf_setmatrix_node
, pdf_setmatrix_node_size
, node_fields_whatsit_pdf_setmatrix
, "pdf_setmatrix", -1 },
415 { pdf_save_node
, pdf_save_node_size
, node_fields_whatsit_pdf_save
, "pdf_save", -1 },
416 { pdf_restore_node
, pdf_restore_node_size
, node_fields_whatsit_pdf_restore
, "pdf_restore", -1 },
418 { -1, -1, NULL, NULL, -1 },
421 #define last_whatsit_node pdf_restore_node
424 When we copy a node list
, there are several possibilities
: we do the same as a new node
,
425 we copy the entry to the table in properties
(a reference
), we do a deep copy of a table
426 in the properties
, we create a new table and give it the original one as a metatable.
427 After some experiments
(that also included timing
) with these scenarios I decided that a
428 deep copy made no sense
, nor did nilling. In the end both the shallow copy and the metatable
429 variant were both ok
, although the second ons is slower. The most important aspect to keep
430 in mind is that references to other nodes in properties no longer can be valid for that
431 copy. We could use two tables
(one unique and one shared
) or metatables but that only
434 When defining a new node
, we could already allocate a table but it is rather easy to do
435 that at the lua end e.g. using a metatable __index method. That way it is under macro
438 When deleting a node
, we could keep the slot
(e.g. setting it to false
) but it could make
439 memory consumption raise unneeded when we have temporary large node lists and after that
442 So
, in the end this is what we ended up with. For the record
, I also experimented with the
445 - copy attributes to the properties so that we have fast access at the lua end
: in the end
446 the overhead is not compensated by speed and convenience
, in fact
, attributes are not
447 that slow when it comes to accessing them
449 - a bitset in the node but again the gain compared to attributes is neglectable and it also
450 demands a pretty string agreement over what bit represents what
, and this is unlikely to
451 succeed in the tex community
(I could use it for font handling
, which is cross package
,
452 but decided that it doesn't pay off
454 In case one wonders why properties make sense then
, well
, it is not so much speed that we
455 gain
, but more convenience
: storing all kind of
(temporary
) data in attributes is no fun and
456 this mechanism makes sure that properties are cleaned up when a node is freed. Also
, the
457 advantage of a more or less global properties table is that we stay at the lua end. An
458 alternative is to store a reference in the node itself but that is complicated by the fact
459 that the register has some limitations
(no numeric keys
) and we also don't want to mess with
463 int lua_properties_level
= 0 ; /* can be private
*/
464 int lua_properties_enabled
= 0 ;
465 int lua_properties_use_metatable
= 0 ;
468 We keep track of nesting so that we don't oveflow the stack
, and
, what is more
469 important
, don't keep resolving the registry index.
472 #define lua_properties_push do
{ \
473 if
(lua_properties_enabled
) { \
474 lua_properties_level
= lua_properties_level
+ 1 ; \
475 if
(lua_properties_level
== 1) { \
476 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
, luaS_index
(node_properties
)); \
477 lua_gettable
(Luas
, LUA_REGISTRYINDEX
); \
482 #define lua_properties_pop do
{ \
483 if
(lua_properties_enabled
) { \
484 if
(lua_properties_level
== 1) \
486 lua_properties_level
= lua_properties_level
- 1 ; \
490 /* No setting is needed
: */
492 #define lua_properties_set
(target
) do
{ \
495 /* Resetting boils down to nilling.
*/
497 #define lua_properties_reset
(target
) do
{ \
498 if
(lua_properties_enabled
) { \
499 if
(lua_properties_level
== 0) { \
500 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
, luaS_index
(node_properties
)); \
501 lua_gettable
(Luas
, LUA_REGISTRYINDEX
); \
503 lua_rawseti
(Luas
,-2,target
); \
507 lua_rawseti
(Luas
,-2,target
); \
513 For a moment I considered supporting all kind of data types but in practice
514 that makes no sense. So we stick to a cheap shallow copy with as option a
515 metatable. Btw
, a deep copy would look like this
:
517 static void copy_lua_table
(lua_State
* L
, int index
) {
520 while
(lua_next
(L
, index-1
) != 0) {
521 lua_pushvalue
(L
, -2);
523 if
(lua_type
(L
,-1)==LUA_TTABLE
)
524 copy_lua_table
(L
,-1);
530 #define lua_properties_copy
(target
, source
) do
{ \
531 if
(lua_properties_enabled
) { \
532 lua_pushinteger
(Luas
,source
); \
533 lua_rawget
(Luas
,-2); \
534 if
(lua_type
(Luas
,-1)==LUA_TTABLE
) { \
535 copy_lua_table
(Luas
,-1); \
536 lua_pushinteger
(Luas
,target
); \
537 lua_insert
(Luas
,-2); \
538 lua_rawset
(Luas
,-3); \
547 /* isn't there a faster way to metatable?
*/
549 #define lua_properties_copy
(target
,source
) do
{ \
550 if
(lua_properties_enabled
) { \
551 if
(lua_properties_level
== 0) { \
552 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
, luaS_index
(node_properties
)); \
553 lua_gettable
(Luas
, LUA_REGISTRYINDEX
); \
554 lua_rawgeti
(Luas
,-1,source
); \
555 if
(lua_type
(Luas
,-1)==LUA_TTABLE
) { \
556 if
(lua_properties_use_metatable
) { \
557 lua_newtable
(Luas
); \
558 lua_insert
(Luas
,-2); \
559 lua_setfield
(Luas
,-2,"__index"); \
560 lua_newtable
(Luas
); \
561 lua_insert
(Luas
,-2); \
562 lua_setmetatable
(Luas
,-2); \
564 lua_rawseti
(Luas
,-2,target
); \
570 lua_rawgeti
(Luas
,-1,source
); \
571 if
(lua_type
(Luas
,-1)==LUA_TTABLE
) { \
572 if
(lua_properties_use_metatable
) { \
573 lua_newtable
(Luas
); \
574 lua_insert
(Luas
,-2); \
575 lua_setfield
(Luas
,-2,"__index"); \
576 lua_newtable
(Luas
); \
577 lua_insert
(Luas
,-2); \
578 lua_setmetatable
(Luas
,-2); \
580 lua_rawseti
(Luas
,-2,target
); \
588 /* Here end the property handlers.
*/
591 int valid_node
(halfword p
)
593 if
(p
> my_prealloc
&& p < var_mem_max) {
594 #ifdef CHECK_NODE_USAGE
595 if
(varmem_sizes
[p
] > 0) {
606 static int test_count
= 1;
608 #define dorangetest
(a
,b
,c
) do
{ \
609 if
(!(b
>=0 && b<c)) { \
610 fprintf
(stdout
,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \
611 (int
)a
, (int
)b
, (int
)c
, __LINE__
,test_count
); \
612 confusion
("node range test failed"); \
615 #define dotest
(a
,b
,c
) do
{ \
617 fprintf
(stdout
,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \
618 (int
)a
, (int
)b
, (int
)c
, __LINE__
,test_count
); \
619 confusion
("node test failed"); \
622 #define check_action_ref
(a
) { dorangetest
(p
,a
,var_mem_max
); }
623 #define check_glue_ref
(a
) { dorangetest
(p
,a
,var_mem_max
); }
624 #define check_attribute_ref
(a
) { dorangetest
(p
,a
,var_mem_max
); }
625 #define check_token_ref
(a
) { confusion
("fuzzy token cleanup in node"); }
627 #ifdef CHECK_NODE_USAGE
629 static void check_static_node_mem
(void
)
631 dotest
(zero_glue
, width
(zero_glue
), 0);
632 dotest
(zero_glue
, type
(zero_glue
), glue_spec_node
);
633 dotest
(zero_glue
, vlink
(zero_glue
), null
);
634 dotest
(zero_glue
, stretch
(zero_glue
), 0);
635 dotest
(zero_glue
, stretch_order
(zero_glue
), normal
);
636 dotest
(zero_glue
, shrink
(zero_glue
), 0);
637 dotest
(zero_glue
, shrink_order
(zero_glue
), normal
);
639 dotest
(sfi_glue
, width
(sfi_glue
), 0);
640 dotest
(sfi_glue
, type
(sfi_glue
), glue_spec_node
);
641 dotest
(sfi_glue
, vlink
(sfi_glue
), null
);
642 dotest
(sfi_glue
, stretch
(sfi_glue
), 0);
643 dotest
(sfi_glue
, stretch_order
(sfi_glue
), sfi
);
644 dotest
(sfi_glue
, shrink
(sfi_glue
), 0);
645 dotest
(sfi_glue
, shrink_order
(sfi_glue
), normal
);
647 dotest
(fil_glue
, width
(fil_glue
), 0);
648 dotest
(fil_glue
, type
(fil_glue
), glue_spec_node
);
649 dotest
(fil_glue
, vlink
(fil_glue
), null
);
650 dotest
(fil_glue
, stretch
(fil_glue
), unity
);
651 dotest
(fil_glue
, stretch_order
(fil_glue
), fil
);
652 dotest
(fil_glue
, shrink
(fil_glue
), 0);
653 dotest
(fil_glue
, shrink_order
(fil_glue
), normal
);
655 dotest
(fill_glue
, width
(fill_glue
), 0);
656 dotest
(fill_glue
, type
(fill_glue
), glue_spec_node
);
657 dotest
(fill_glue
, vlink
(fill_glue
), null
);
658 dotest
(fill_glue
, stretch
(fill_glue
), unity
);
659 dotest
(fill_glue
, stretch_order
(fill_glue
), fill
);
660 dotest
(fill_glue
, shrink
(fill_glue
), 0);
661 dotest
(fill_glue
, shrink_order
(fill_glue
), normal
);
663 dotest
(ss_glue
, width
(ss_glue
), 0);
664 dotest
(ss_glue
, type
(ss_glue
), glue_spec_node
);
665 dotest
(ss_glue
, vlink
(ss_glue
), null
);
666 dotest
(ss_glue
, stretch
(ss_glue
), unity
);
667 dotest
(ss_glue
, stretch_order
(ss_glue
), fil
);
668 dotest
(ss_glue
, shrink
(ss_glue
), unity
);
669 dotest
(ss_glue
, shrink_order
(ss_glue
), fil
);
671 dotest
(fil_neg_glue
, width
(fil_neg_glue
), 0);
672 dotest
(fil_neg_glue
, type
(fil_neg_glue
), glue_spec_node
);
673 dotest
(fil_neg_glue
, vlink
(fil_neg_glue
), null
);
674 dotest
(fil_neg_glue
, stretch
(fil_neg_glue
), -unity
);
675 dotest
(fil_neg_glue
, stretch_order
(fil_neg_glue
), fil
);
676 dotest
(fil_neg_glue
, shrink
(fil_neg_glue
), 0);
677 dotest
(fil_neg_glue
, shrink_order
(fil_neg_glue
), normal
);
680 static void node_mem_dump
(halfword p
)
683 for
(r
= my_prealloc
+ 1; r
< var_mem_max
; r
++) {
686 while
(s
> my_prealloc
&& varmem_sizes[s] == 0) {
692 && (r - s) < get_node_size(type(s), subtype(s))
694 if
(type
(s
) == disc_node
) {
695 fprintf
(stdout
," pointed to from %s node %d (vlink %d, alink %d): ",
696 get_node_name
(type
(s
), subtype
(s
)), (int
) s
,
697 (int
) vlink
(s
), (int
) alink
(s
));
698 fprintf
(stdout
, "pre_break(%d,%d,%d), ",
699 (int
) vlink_pre_break
(s
), (int
) tlink
(pre_break
(s
)),
700 (int
) alink
(pre_break
(s
)));
701 fprintf
(stdout
, "post_break(%d,%d,%d), ",
702 (int
) vlink_post_break
(s
),
703 (int
) tlink
(post_break
(s
)),
704 (int
) alink
(post_break
(s
)));
705 fprintf
(stdout
, "no_break(%d,%d,%d)",
706 (int
) vlink_no_break
(s
), (int
) tlink
(no_break
(s
)),
707 (int
) alink
(no_break
(s
)));
708 fprintf
(stdout
, "\n");
711 ||
(type
(s
) == glyph_node
&& lig_ptr (s) == p)
712 ||
(type
(s
) == vlist_node
&& list_ptr(s) == p)
713 ||
(type
(s
) == hlist_node
&& list_ptr(s) == p)
714 ||
(type
(s
) == unset_node
&& list_ptr(s) == p)
715 ||
(type
(s
) == ins_node
&& ins_ptr (s) == p)
717 fprintf
(stdout
," pointed to from %s node %d (vlink %d, alink %d): ",
718 get_node_name
(type
(s
), subtype
(s
)), (int
) s
,
719 (int
) vlink
(s
), (int
) alink
(s
));
720 if
(type
(s
) == glyph_node
) {
721 fprintf
(stdout
, "lig_ptr(%d)", (int
) lig_ptr
(s
));
722 } else if
(type
(s
) == vlist_node || type
(s
) == hlist_node
) {
723 fprintf
(stdout
, "list_ptr(%d)", (int
) list_ptr
(s
));
725 fprintf
(stdout
, "\n");
727 if
((type
(s
) != penalty_node
) && (type(s) != math_node) && (type(s) != kern_node)) {
728 fprintf
(stdout
, " pointed to from %s node %d\n",
729 get_node_name
(type
(s
), subtype
(s
)), (int
) s
);
740 static int free_error
(halfword p
)
742 if
(p
> my_prealloc
&& p < var_mem_max) {
743 #ifdef CHECK_NODE_USAGE
745 if
(varmem_sizes
[p
] == 0) {
746 check_static_node_mem
();
747 for
(i
= (my_prealloc
+ 1); i
< var_mem_max
; i
++) {
748 if
(varmem_sizes
[i
] > 0) {
753 if
(type
(p
) == glyph_node
) {
754 formatted_error
("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int
) character
(p
), (int
) p
);
756 formatted_error
("nodes", "attempt to double-free %s node %d, ignored", get_node_name
(type
(p
), subtype
(p
)), (int
) p
);
763 formatted_error
("nodes", "attempt to free an impossible node %d", (int
) p
);
770 static int copy_error
(halfword p
)
772 if
(p
>= 0 && p < var_mem_max) {
773 #ifdef CHECK_NODE_USAGE
774 if
(p
> my_prealloc
&& varmem_sizes[p] == 0) {
775 if
(type
(p
) == glyph_node
) {
776 formatted_warning
("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int
) character
(p
), (int
) p
);
778 formatted_warning
("nodes", "attempt to copy free %s node %d, ignored", get_node_name
(type
(p
), subtype
(p
)), (int
) p
);
784 formatted_error
("nodes", "attempt to copy an impossible node %d", (int
) p
);
791 halfword new_node
(int i
, int j
)
793 int s
= get_node_size
(i
, j
);
794 halfword n
= get_node
(s
);
796 It should be possible to do this memset at |free_node
()|.
798 Both type
() and subtype
() will be set below
, and vlink
() is
799 set to null by |get_node
()|
, so we can do we clearing one
802 (void
) memset
((void
*) (varmem
+ n
+ 1), 0, (sizeof
(memory_word
) * ((unsigned
) s
- 1)));
812 pre_break
(n
) = pre_break_head
(n
);
813 type
(pre_break
(n
)) = nesting_node
;
814 subtype
(pre_break
(n
)) = pre_break_head
(0);
815 post_break
(n
) = post_break_head
(n
);
816 type
(post_break
(n
)) = nesting_node
;
817 subtype
(post_break
(n
)) = post_break_head
(0);
818 no_break
(n
) = no_break_head
(n
);
819 type
(no_break
(n
)) = nesting_node
;
820 subtype
(no_break
(n
)) = no_break_head
(0);
823 depth
(n
) = null_flag
;
824 height
(n
) = null_flag
;
825 width
(n
) = null_flag
;
828 rule_transform
(n
) = 0;
831 if
(j
== open_node
) {
832 open_name
(n
) = get_nullstr
();
833 open_area
(n
) = open_name
(n
);
834 open_ext
(n
) = open_name
(n
);
838 width
(n
) = null_flag
;
840 case pseudo_line_node
:
842 /* this is a trick that makes |pseudo_files| slightly slower
,
843 but the overall allocation faster then an explicit test
844 at the top of |new_node
()|.
847 free_node
(n
, variable_node_size
);
848 n
= slow_get_node
(j
);
849 (void
) memset
((void
*) (varmem
+ n
+ 1), 0, (sizeof
(memory_word
) * ((unsigned
) j
- 1)));
855 if
(int_par
(synctex_code
)) {
856 /* handle synctex extension
*/
859 synctex_tag_math
(n
) = cur_input.synctex_tag_field
;
860 synctex_line_math
(n
) = line
;
863 synctex_tag_glue
(n
) = cur_input.synctex_tag_field
;
864 synctex_line_glue
(n
) = line
;
868 synctex_tag_kern
(n
) = cur_input.synctex_tag_field
;
869 synctex_line_kern
(n
) = line
;
875 synctex_tag_box
(n
) = cur_input.synctex_tag_field
;
876 synctex_line_box
(n
) = line
;
879 synctex_tag_rule
(n
) = cur_input.synctex_tag_field
;
880 synctex_line_rule
(n
) = line
;
884 /* take care of attributes
*/
885 if
(nodetype_has_attributes
(i
)) {
886 build_attribute_list
(n
);
887 /* lua_properties_set
*/
889 type
(n
) = (quarterword
) i
;
890 subtype
(n
) = (quarterword
) j
;
894 halfword raw_glyph_node
(void
)
896 register halfword n
= get_node
(glyph_node_size
);
897 (void
) memset
((void
*) (varmem
+ n
+ 1), 0, (sizeof
(memory_word
) * (glyph_node_size
- 1)));
898 type
(n
) = glyph_node
;
903 halfword new_glyph_node
(void
)
905 register halfword n
= get_node
(glyph_node_size
);
906 (void
) memset
((void
*) (varmem
+ n
+ 1), 0, (sizeof
(memory_word
) * (glyph_node_size
- 1)));
907 type
(n
) = glyph_node
;
909 build_attribute_list
(n
);
910 /* lua_properties_set
*/
914 @ makes a duplicate of the node list that starts at |p| and returns a
915 pointer to the new list
918 halfword do_copy_node_list
(halfword p
, halfword end
)
920 halfword q
= null
; /* previous position in new list
*/
921 halfword h
= null
; /* head of the list
*/
922 register halfword s
;
923 lua_properties_push
; /* saves stack and time
*/
934 lua_properties_pop
; /* saves stack and time
*/
938 halfword copy_node_list
(halfword p
)
940 return do_copy_node_list
(p
, null
);
943 #define copy_sub_list
(target
,source
) do
{ \
944 if
(source
!= null
) { \
945 s
= do_copy_node_list
(source
, null
); \
952 #define copy_sub_node
(target
,source
) do
{ \
953 if
(source
!= null
) { \
954 s
= copy_node
(source
); \
961 @ make a dupe of a single node
964 static void copy_node_wrapup_core
(halfword p
, halfword r
)
967 switch
(subtype
(p
)) {
970 add_token_ref
(write_tokens
(p
));
975 case user_defined_node
:
976 switch
(user_node_type
(p
)) {
978 add_node_attr_ref
(user_node_value
(p
));
984 s
= copy_node_list
(user_node_value
(p
));
985 user_node_value
(r
) = s
;
988 /* |add_string_ref
(user_node_value
(p
));|
*/
991 add_token_ref
(user_node_value
(p
));
1000 void copy_node_wrapup_dvi
(halfword p
, halfword r
)
1004 void copy_node_wrapup_pdf
(halfword p
, halfword r
)
1006 switch
(subtype
(p
)) {
1007 case pdf_literal_node
:
1008 copy_pdf_literal
(r
, p
);
1010 case pdf_colorstack_node
:
1011 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
1012 add_token_ref
(pdf_colorstack_data
(p
));
1014 case pdf_setmatrix_node
:
1015 add_token_ref
(pdf_setmatrix_data
(p
));
1017 case pdf_annot_node
:
1018 add_token_ref
(pdf_annot_data
(p
));
1020 case pdf_start_link_node
:
1021 if
(pdf_link_attr
(r
) != null
)
1022 add_token_ref
(pdf_link_attr
(r
));
1023 add_action_ref
(pdf_link_action
(r
));
1026 if
(pdf_dest_named_id
(p
) > 0)
1027 add_token_ref
(pdf_dest_id
(p
));
1029 case pdf_thread_node
:
1030 case pdf_start_thread_node
:
1031 if
(pdf_thread_named_id
(p
) > 0)
1032 add_token_ref
(pdf_thread_id
(p
));
1033 if
(pdf_thread_attr
(p
) != null
)
1034 add_token_ref
(pdf_thread_attr
(p
));
1041 halfword copy_node
(const halfword p
)
1043 halfword r
; /* current node being fabricated for new list
*/
1044 halfword w
; /* whatsit subtype
*/
1045 register halfword s
; /* a helper variable for copying into variable mem
*/
1047 if
(copy_error
(p
)) {
1048 r
= new_node
(temp_node
, 0);
1051 i
= get_node_size
(type
(p
), subtype
(p
));
1054 (void
) memcpy
((void
*) (varmem
+ r
), (void
*) (varmem
+ p
), (sizeof
(memory_word
) * (unsigned
) i
));
1056 if
(int_par
(synctex_code
)) {
1057 /* handle synctex extension
*/
1060 synctex_tag_math
(r
) = cur_input.synctex_tag_field
;
1061 synctex_line_math
(r
) = line
;
1064 synctex_tag_kern
(r
) = cur_input.synctex_tag_field
;
1065 synctex_line_kern
(r
) = line
;
1069 if
(nodetype_has_attributes
(type
(p
))) {
1070 add_node_attr_ref
(node_attr
(p
));
1072 lua_properties_copy
(r
,p
);
1078 copy_sub_list
(lig_ptr
(r
),lig_ptr
(p
)) ;
1081 add_glue_ref
(glue_ptr
(p
));
1082 copy_sub_list
(leader_ptr
(r
),leader_ptr
(p
)) ;
1087 copy_sub_list
(list_ptr
(r
),list_ptr
(p
)) ;
1090 pre_break
(r
) = pre_break_head
(r
);
1091 if
(vlink_pre_break
(p
) != null
) {
1092 s
= copy_node_list
(vlink_pre_break
(p
));
1093 alink
(s
) = pre_break
(r
);
1094 tlink_pre_break
(r
) = tail_of_list
(s
);
1095 vlink_pre_break
(r
) = s
;
1097 assert
(tlink
(pre_break
(r
)) == null
);
1099 post_break
(r
) = post_break_head
(r
);
1100 if
(vlink_post_break
(p
) != null
) {
1101 s
= copy_node_list
(vlink_post_break
(p
));
1102 alink
(s
) = post_break
(r
);
1103 tlink_post_break
(r
) = tail_of_list
(s
);
1104 vlink_post_break
(r
) = s
;
1106 assert
(tlink_post_break
(r
) == null
);
1108 no_break
(r
) = no_break_head
(r
);
1109 if
(vlink
(no_break
(p
)) != null
) {
1110 s
= copy_node_list
(vlink_no_break
(p
));
1111 alink
(s
) = no_break
(r
);
1112 tlink_no_break
(r
) = tail_of_list
(s
);
1113 vlink_no_break
(r
) = s
;
1115 assert
(tlink_no_break
(r
) == null
);
1119 if
(glue_ptr
(p
) != zero_glue
) {
1120 add_glue_ref
(glue_ptr
(p
));
1124 add_glue_ref
(split_top_ptr
(p
));
1125 copy_sub_list
(ins_ptr
(r
),ins_ptr
(p
)) ;
1127 case margin_kern_node
:
1128 copy_sub_node
(margin_char
(r
),margin_char
(p
));
1131 add_token_ref
(mark_ptr
(p
));
1134 copy_sub_list
(adjust_ptr
(r
),adjust_ptr
(p
));
1137 copy_sub_list
(display_mlist
(r
),display_mlist
(p
)) ;
1138 copy_sub_list
(text_mlist
(r
),text_mlist
(p
)) ;
1139 copy_sub_list
(script_mlist
(r
),script_mlist
(p
)) ;
1140 copy_sub_list
(script_script_mlist
(r
),script_script_mlist
(p
)) ;
1143 copy_sub_list
(nucleus
(r
),nucleus
(p
)) ;
1144 copy_sub_list
(subscr
(r
),subscr
(p
)) ;
1145 copy_sub_list
(supscr
(r
),supscr
(p
)) ;
1148 copy_sub_list
(nucleus
(r
),nucleus
(p
)) ;
1149 copy_sub_list
(subscr
(r
),subscr
(p
)) ;
1150 copy_sub_list
(supscr
(r
),supscr
(p
)) ;
1151 copy_sub_node
(left_delimiter
(r
),left_delimiter
(p
)) ;
1152 copy_sub_list
(degree
(r
),degree
(p
)) ;
1155 copy_sub_list
(nucleus
(r
),nucleus
(p
)) ;
1156 copy_sub_list
(subscr
(r
),subscr
(p
)) ;
1157 copy_sub_list
(supscr
(r
),supscr
(p
)) ;
1158 copy_sub_list
(top_accent_chr
(r
),top_accent_chr
(p
)) ;
1159 copy_sub_list
(bot_accent_chr
(r
),bot_accent_chr
(p
)) ;
1160 copy_sub_list
(overlay_accent_chr
(r
),overlay_accent_chr
(p
)) ;
1163 copy_sub_node
(delimiter
(r
),delimiter
(p
)) ;
1166 case sub_mlist_node
:
1167 copy_sub_list
(math_list
(r
),math_list
(p
)) ;
1170 copy_sub_list
(numerator
(r
),numerator
(p
)) ;
1171 copy_sub_list
(denominator
(r
),denominator
(p
)) ;
1172 copy_sub_node
(left_delimiter
(r
),left_delimiter
(p
)) ;
1173 copy_sub_node
(right_delimiter
(r
),right_delimiter
(p
)) ;
1175 case glue_spec_node
:
1176 glue_ref_count
(r
) = null
;
1179 case local_par_node
:
1184 if
(w
>= backend_first_pdf_whatsit
) {
1185 copy_node_wrapup_pdf
(p
,r
);
1186 } else if
(w
>= backend_first_dvi_whatsit
) {
1187 copy_node_wrapup_dvi
(p
,r
);
1189 copy_node_wrapup_core
(p
,r
);
1198 #define free_sub_list
(source
) if
(source
!= null
) flush_node_list
(source
);
1199 #define free_sub_node
(source
) if
(source
!= null
) flush_node
(source
);
1203 static void flush_node_wrapup_core
(halfword p
)
1205 switch
(subtype
(p
)) {
1212 delete_token_ref
(write_tokens
(p
));
1217 case user_defined_node
:
1218 switch
(user_node_type
(p
)) {
1220 delete_attribute_ref
(user_node_value
(p
));
1225 flush_node_list
(user_node_value
(p
));
1228 /* |delete_string_ref
(user_node_value
(p
));|
*//* if this was mpost ..
*/
1231 delete_token_ref
(user_node_value
(p
));
1235 const char
*hlp
[] = {
1236 "The type of the value in a user defined whatsit node should be one",
1237 "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),",
1238 "or 't' (tokenlist). Yours has an unknown type, and therefore I don't",
1239 "know how to free the node's value. A memory leak may result.",
1242 tex_error
("Unidentified user defined whatsit", hlp
);
1250 void flush_node_wrapup_dvi
(halfword p
)
1254 void flush_node_wrapup_pdf
(halfword p
)
1256 switch
(subtype
(p
)) {
1258 case pdf_restore_node
:
1259 case pdf_refobj_node
:
1260 case pdf_end_link_node
:
1261 case pdf_end_thread_node
:
1263 case pdf_literal_node
:
1264 free_pdf_literal
(p
);
1266 case pdf_colorstack_node
:
1267 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
1268 delete_token_ref
(pdf_colorstack_data
(p
));
1270 case pdf_setmatrix_node
:
1271 delete_token_ref
(pdf_setmatrix_data
(p
));
1273 case pdf_annot_node
:
1274 delete_token_ref
(pdf_annot_data
(p
));
1276 case pdf_link_data_node
:
1278 case pdf_start_link_node
:
1279 if
(pdf_link_attr
(p
) != null
)
1280 delete_token_ref
(pdf_link_attr
(p
));
1281 delete_action_ref
(pdf_link_action
(p
));
1284 if
(pdf_dest_named_id
(p
) > 0)
1285 delete_token_ref
(pdf_dest_id
(p
));
1287 case pdf_action_node
:
1288 if
(pdf_action_type
(p
) == pdf_action_user
) {
1289 delete_token_ref
(pdf_action_tokens
(p
));
1291 if
(pdf_action_file
(p
) != null
)
1292 delete_token_ref
(pdf_action_file
(p
));
1293 if
(pdf_action_type
(p
) == pdf_action_page
)
1294 delete_token_ref
(pdf_action_tokens
(p
));
1295 else if
(pdf_action_named_id
(p
) > 0)
1296 delete_token_ref
(pdf_action_id
(p
));
1299 case pdf_thread_data_node
:
1301 case pdf_thread_node
:
1302 case pdf_start_thread_node
:
1303 if
(pdf_thread_named_id
(p
) > 0)
1304 delete_token_ref
(pdf_thread_id
(p
));
1305 if
(pdf_thread_attr
(p
) != null
)
1306 delete_token_ref
(pdf_thread_attr
(p
));
1311 void flush_node
(halfword p
)
1314 if
(p
== null
) /* legal
, but no-op
*/
1320 free_sub_list
(lig_ptr
(p
));
1323 delete_glue_ref
(glue_ptr
(p
));
1324 free_sub_list
(leader_ptr
(p
));
1329 free_sub_list
(list_ptr
(p
));
1332 /* watch the start at temp node hack
*/
1333 free_sub_list
(vlink
(pre_break
(p
)));
1334 free_sub_list
(vlink
(post_break
(p
)));
1335 free_sub_list
(vlink
(no_break
(p
)));
1342 /* begin mathskip code
*/
1343 if
(glue_ptr
(p
) != zero_glue
) {
1344 delete_glue_ref
(glue_ptr
(p
));
1346 /* end mathskip code
*/
1348 case glue_spec_node
:
1349 /* this allows free-ing of lua-allocated glue specs
*/
1350 if
(valid_node
(p
)) {
1351 if
(glue_ref_count
(p
)!=null
) {
1352 decr
(glue_ref_count
(p
));
1354 free_node
(p
, get_node_size
(type
(p
), subtype
(p
)));
1360 case local_par_node
:
1365 if
(w
>= backend_first_pdf_whatsit
) {
1366 flush_node_wrapup_pdf
(p
);
1367 } else if
(w
>= backend_first_dvi_whatsit
) {
1368 flush_node_wrapup_dvi
(p
);
1370 flush_node_wrapup_core
(p
);
1374 flush_node_list
(ins_ptr
(p
));
1375 delete_glue_ref
(split_top_ptr
(p
));
1377 case margin_kern_node
:
1378 flush_node
(margin_char
(p
));
1381 delete_token_ref
(mark_ptr
(p
));
1384 flush_node_list
(adjust_ptr
(p
));
1386 case style_node
: /* nothing to do
*/
1389 free_sub_list
(display_mlist
(p
));
1390 free_sub_list
(text_mlist
(p
));
1391 free_sub_list
(script_mlist
(p
));
1392 free_sub_list
(script_script_mlist
(p
));
1395 free_sub_list
(nucleus
(p
));
1396 free_sub_list
(subscr
(p
));
1397 free_sub_list
(supscr
(p
));
1400 free_sub_list
(nucleus
(p
));
1401 free_sub_list
(subscr
(p
));
1402 free_sub_list
(supscr
(p
));
1403 free_sub_node
(left_delimiter
(p
));
1404 free_sub_list
(degree
(p
));
1407 free_sub_list
(nucleus
(p
));
1408 free_sub_list
(subscr
(p
));
1409 free_sub_list
(supscr
(p
));
1410 free_sub_list
(top_accent_chr
(p
));
1411 free_sub_list
(bot_accent_chr
(p
));
1412 free_sub_list
(overlay_accent_chr
(p
));
1415 free_sub_list
(delimiter
(p
));
1417 case delim_node
: /* nothing to do
*/
1418 case math_char_node
:
1419 case math_text_char_node
:
1422 case sub_mlist_node
:
1423 free_sub_list
(math_list
(p
));
1426 free_sub_list
(numerator
(p
));
1427 free_sub_list
(denominator
(p
));
1428 free_sub_node
(left_delimiter
(p
));
1429 free_sub_node
(right_delimiter
(p
));
1431 case pseudo_file_node
:
1432 free_sub_list
(pseudo_lines
(p
));
1434 case pseudo_line_node
:
1436 free_node
(p
, subtype
(p
));
1439 case align_stack_node
:
1444 case unhyphenated_node
:
1445 case hyphenated_node
:
1448 case inserting_node
:
1451 case attribute_node
:
1452 case attribute_list_node
:
1456 formatted_error
("nodes","flushing weird node type %d", type
(p
));
1459 if
(nodetype_has_attributes
(type
(p
))) {
1460 delete_attribute_ref
(node_attr
(p
));
1461 lua_properties_reset
(p
);
1463 free_node
(p
, get_node_size
(type
(p
), subtype
(p
)));
1468 void flush_node_list
(halfword pp
)
1469 { /* erase list of nodes starting at |p|
*/
1470 register halfword p
= pp
;
1471 if
(p
== null
) /* legal
, but no-op
*/
1475 lua_properties_push
; /* saves stack and time
*/
1477 register halfword q
= vlink
(p
);
1481 lua_properties_pop
; /* saves stack and time
*/
1485 static void check_node_wrapup_core
(halfword p
)
1487 switch
(subtype
(p
)) {
1490 check_token_ref
(write_tokens
(p
));
1492 case user_defined_node
:
1493 switch
(user_node_type
(p
)) {
1495 check_attribute_ref
(user_node_value
(p
));
1498 check_token_ref
(user_node_value
(p
));
1501 dorangetest
(p
, user_node_value
(p
), var_mem_max
);
1507 confusion
("unknown user node type");
1519 void check_node_wrapup_dvi
(halfword p
)
1523 void check_node_wrapup_pdf
(halfword p
)
1525 switch
(subtype
(p
)) {
1526 case pdf_literal_node
:
1527 if
(pdf_literal_type
(p
) == normal
)
1528 check_token_ref
(pdf_literal_data
(p
));
1530 case pdf_colorstack_node
:
1531 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
1532 check_token_ref
(pdf_colorstack_data
(p
));
1534 case pdf_setmatrix_node
:
1535 check_token_ref
(pdf_setmatrix_data
(p
));
1538 if
(late_lua_name
(p
) > 0)
1539 check_token_ref
(late_lua_name
(p
));
1540 if
(late_lua_type
(p
) == normal
)
1541 check_token_ref
(late_lua_data
(p
));
1543 case pdf_annot_node
:
1544 check_token_ref
(pdf_annot_data
(p
));
1546 case pdf_start_link_node
:
1547 if
(pdf_link_attr
(p
) != null
)
1548 check_token_ref
(pdf_link_attr
(p
));
1549 check_action_ref
(pdf_link_action
(p
));
1552 if
(pdf_dest_named_id
(p
) > 0)
1553 check_token_ref
(pdf_dest_id
(p
));
1555 case pdf_thread_node
:
1556 case pdf_start_thread_node
:
1557 if
(pdf_thread_named_id
(p
) > 0)
1558 check_token_ref
(pdf_thread_id
(p
));
1559 if
(pdf_thread_attr
(p
) != null
)
1560 check_token_ref
(pdf_thread_attr
(p
));
1563 case pdf_restore_node
:
1564 case pdf_refobj_node
:
1565 case pdf_end_link_node
:
1566 case pdf_end_thread_node
:
1569 confusion
("wrapup pdf nodes");
1574 void check_node
(halfword p
)
1579 dorangetest
(p
, lig_ptr
(p
), var_mem_max
);
1582 check_glue_ref
(glue_ptr
(p
));
1583 dorangetest
(p
, leader_ptr
(p
), var_mem_max
);
1588 case align_record_node
:
1589 dorangetest
(p
, list_ptr
(p
), var_mem_max
);
1592 dorangetest
(p
, ins_ptr
(p
), var_mem_max
);
1593 check_glue_ref
(split_top_ptr
(p
));
1597 if
(w
>= backend_first_pdf_whatsit
) {
1598 check_node_wrapup_pdf
(p
);
1599 } else if
(w
>= backend_first_dvi_whatsit
) {
1600 check_node_wrapup_dvi
(p
);
1602 check_node_wrapup_core
(p
);
1605 case margin_kern_node
:
1606 check_node
(margin_char
(p
));
1609 /* begin mathskip code
*/
1610 if
(glue_ptr
(p
) != zero_glue
) {
1611 check_glue_ref
(glue_ptr
(p
));
1613 /* end mathskip code
*/
1616 dorangetest
(p
, vlink
(pre_break
(p
)), var_mem_max
);
1617 dorangetest
(p
, vlink
(post_break
(p
)), var_mem_max
);
1618 dorangetest
(p
, vlink
(no_break
(p
)), var_mem_max
);
1621 dorangetest
(p
, adjust_ptr
(p
), var_mem_max
);
1623 case pseudo_file_node
:
1624 dorangetest
(p
, pseudo_lines
(p
), var_mem_max
);
1626 case pseudo_line_node
:
1630 dorangetest
(p
, display_mlist
(p
), var_mem_max
);
1631 dorangetest
(p
, text_mlist
(p
), var_mem_max
);
1632 dorangetest
(p
, script_mlist
(p
), var_mem_max
);
1633 dorangetest
(p
, script_script_mlist
(p
), var_mem_max
);
1636 dorangetest
(p
, numerator
(p
), var_mem_max
);
1637 dorangetest
(p
, denominator
(p
), var_mem_max
);
1638 dorangetest
(p
, left_delimiter
(p
), var_mem_max
);
1639 dorangetest
(p
, right_delimiter
(p
), var_mem_max
);
1642 dorangetest
(p
, nucleus
(p
), var_mem_max
);
1643 dorangetest
(p
, subscr
(p
), var_mem_max
);
1644 dorangetest
(p
, supscr
(p
), var_mem_max
);
1647 dorangetest
(p
, nucleus
(p
), var_mem_max
);
1648 dorangetest
(p
, subscr
(p
), var_mem_max
);
1649 dorangetest
(p
, supscr
(p
), var_mem_max
);
1650 dorangetest
(p
, degree
(p
), var_mem_max
);
1651 dorangetest
(p
, left_delimiter
(p
), var_mem_max
);
1654 dorangetest
(p
, nucleus
(p
), var_mem_max
);
1655 dorangetest
(p
, subscr
(p
), var_mem_max
);
1656 dorangetest
(p
, supscr
(p
), var_mem_max
);
1657 dorangetest
(p
, top_accent_chr
(p
), var_mem_max
);
1658 dorangetest
(p
, bot_accent_chr
(p
), var_mem_max
);
1659 dorangetest
(p
, overlay_accent_chr
(p
), var_mem_max
);
1662 dorangetest
(p
, delimiter
(p
), var_mem_max
);
1670 case attribute_list_node
:
1671 case attribute_node
:
1672 case glue_spec_node
:
1674 case align_stack_node
:
1679 case unhyphenated_node
:
1680 case hyphenated_node
:
1686 case local_par_node
:
1689 fprintf
(stdout
, "check_node: type is %d\n", type
(p
));
1695 void fix_node_list
(halfword head
)
1710 halfword get_node
(int s
)
1712 register halfword r
;
1714 if
(s
< MAX_CHAIN_SIZE
) {
1717 free_chain
[s
] = vlink
(r
);
1718 #ifdef CHECK_NODE_USAGE
1719 varmem_sizes
[r
] = (char
) s
;
1722 var_used
+= s
; /* maintain usage statistics
*/
1725 /* this is the end of the 'inner loop'
*/
1726 return slow_get_node
(s
);
1728 normal_error
("nodes","there is a problem in getting a node, case 1");
1734 void free_node
(halfword p
, int s
)
1736 if
(p
<= my_prealloc
) {
1737 formatted_error
("nodes", "node number %d of type %d should not be freed", (int
) p
, type
(p
));
1740 #ifdef CHECK_NODE_USAGE
1741 varmem_sizes
[p
] = 0;
1743 if
(s
< MAX_CHAIN_SIZE
) {
1744 vlink
(p
) = free_chain
[s
];
1747 /* todo ? it is perhaps possible to merge this node with an existing rover
*/
1750 while
(vlink
(rover
) != vlink
(p
)) {
1751 rover
= vlink
(rover
);
1755 /* maintain statistics
*/
1760 static void free_node_chain
(halfword q
, int s
)
1762 register halfword p
= q
;
1763 while
(vlink
(p
) != null
) {
1764 #ifdef CHECK_NODE_USAGE
1765 varmem_sizes
[p
] = 0;
1771 #ifdef CHECK_NODE_USAGE
1772 varmem_sizes
[p
] = 0;
1774 vlink
(p
) = free_chain
[s
];
1779 void init_node_mem
(int t
)
1781 my_prealloc
= var_mem_stat_max
;
1785 assert
(whatsit_node_data
[user_defined_node
].id
== user_defined_node
);
1786 assert
(node_data
[passive_node
].id
== passive_node
);
1789 varmem
= (memory_word
*) realloc
((void
*) varmem
, sizeof
(memory_word
) * (unsigned
) t
);
1790 if
(varmem
== NULL) {
1791 overflow
("node memory size", (unsigned
) var_mem_max
);
1793 memset
((void
*) (varmem
), 0, (unsigned
) t
* sizeof
(memory_word
));
1794 #ifdef CHECK_NODE_USAGE
1795 varmem_sizes
= (char
*) realloc
(varmem_sizes
, sizeof
(char
) * (unsigned
) t
);
1796 if
(varmem_sizes
== NULL) {
1797 overflow
("node memory size", (unsigned
) var_mem_max
);
1799 memset
((void
*) varmem_sizes
, 0, sizeof
(char
) * (unsigned
) t
);
1802 rover
= var_mem_stat_max
+ 1;
1803 vlink
(rover
) = rover
;
1804 node_size
(rover
) = (t
- rover
);
1806 /* initialize static glue specs
*/
1807 glue_ref_count
(zero_glue
) = null
+ 1;
1808 width
(zero_glue
) = 0;
1809 type
(zero_glue
) = glue_spec_node
;
1810 vlink
(zero_glue
) = null
;
1811 stretch
(zero_glue
) = 0;
1812 stretch_order
(zero_glue
) = normal
;
1813 shrink
(zero_glue
) = 0;
1814 shrink_order
(zero_glue
) = normal
;
1815 glue_ref_count
(sfi_glue
) = null
+ 1;
1816 width
(sfi_glue
) = 0;
1817 type
(sfi_glue
) = glue_spec_node
;
1818 vlink
(sfi_glue
) = null
;
1819 stretch
(sfi_glue
) = 0;
1820 stretch_order
(sfi_glue
) = sfi
;
1821 shrink
(sfi_glue
) = 0;
1822 shrink_order
(sfi_glue
) = normal
;
1823 glue_ref_count
(fil_glue
) = null
+ 1;
1824 width
(fil_glue
) = 0;
1825 type
(fil_glue
) = glue_spec_node
;
1826 vlink
(fil_glue
) = null
;
1827 stretch
(fil_glue
) = unity
;
1828 stretch_order
(fil_glue
) = fil
;
1829 shrink
(fil_glue
) = 0;
1830 shrink_order
(fil_glue
) = normal
;
1831 glue_ref_count
(fill_glue
) = null
+ 1;
1832 width
(fill_glue
) = 0;
1833 type
(fill_glue
) = glue_spec_node
;
1834 vlink
(fill_glue
) = null
;
1835 stretch
(fill_glue
) = unity
;
1836 stretch_order
(fill_glue
) = fill
;
1837 shrink
(fill_glue
) = 0;
1838 shrink_order
(fill_glue
) = normal
;
1839 glue_ref_count
(ss_glue
) = null
+ 1;
1841 type
(ss_glue
) = glue_spec_node
;
1842 vlink
(ss_glue
) = null
;
1843 stretch
(ss_glue
) = unity
;
1844 stretch_order
(ss_glue
) = fil
;
1845 shrink
(ss_glue
) = unity
;
1846 shrink_order
(ss_glue
) = fil
;
1847 glue_ref_count
(fil_neg_glue
) = null
+ 1;
1848 width
(fil_neg_glue
) = 0;
1849 type
(fil_neg_glue
) = glue_spec_node
;
1850 vlink
(fil_neg_glue
) = null
;
1851 stretch
(fil_neg_glue
) = -unity
;
1852 stretch_order
(fil_neg_glue
) = fil
;
1853 shrink
(fil_neg_glue
) = 0;
1854 shrink_order
(fil_neg_glue
) = normal
;
1855 /* initialize node list heads
*/
1856 vinfo
(page_ins_head
) = 0;
1857 type
(page_ins_head
) = temp_node
;
1858 vlink
(page_ins_head
) = null
;
1859 alink
(page_ins_head
) = null
;
1860 vinfo
(contrib_head
) = 0;
1861 type
(contrib_head
) = temp_node
;
1862 vlink
(contrib_head
) = null
;
1863 alink
(contrib_head
) = null
;
1864 vinfo
(page_head
) = 0;
1865 type
(page_head
) = temp_node
;
1866 vlink
(page_head
) = null
;
1867 alink
(page_head
) = null
;
1868 vinfo
(temp_head
) = 0;
1869 type
(temp_head
) = temp_node
;
1870 vlink
(temp_head
) = null
;
1871 alink
(temp_head
) = null
;
1872 vinfo
(hold_head
) = 0;
1873 type
(hold_head
) = temp_node
;
1874 vlink
(hold_head
) = null
;
1875 alink
(hold_head
) = null
;
1876 vinfo
(adjust_head
) = 0;
1877 type
(adjust_head
) = temp_node
;
1878 vlink
(adjust_head
) = null
;
1879 alink
(adjust_head
) = null
;
1880 vinfo
(pre_adjust_head
) = 0;
1881 type
(pre_adjust_head
) = temp_node
;
1882 vlink
(pre_adjust_head
) = null
;
1883 alink
(pre_adjust_head
) = null
;
1885 type
(active
) = unhyphenated_node
;
1886 vlink
(active
) = null
;
1887 alink
(active
) = null
;
1888 vinfo
(align_head
) = 0;
1889 type
(align_head
) = temp_node
;
1890 vlink
(align_head
) = null
;
1891 alink
(align_head
) = null
;
1892 vinfo
(end_span
) = 0;
1893 type
(end_span
) = span_node
;
1894 vlink
(end_span
) = null
;
1895 alink
(end_span
) = null
;
1896 type
(begin_point
) = glyph_node
;
1897 subtype
(begin_point
) = 0;
1898 vlink
(begin_point
) = null
;
1899 vinfo
(begin_point
+ 1) = null
;
1900 alink
(begin_point
) = null
;
1901 font
(begin_point
) = 0;
1902 character
(begin_point
) = '.'
;
1903 vinfo
(begin_point
+ 3) = 0;
1904 vlink
(begin_point
+ 3) = 0;
1905 vinfo
(begin_point
+ 4) = 0;
1906 vlink
(begin_point
+ 4) = 0;
1907 type
(end_point
) = glyph_node
;
1908 subtype
(end_point
) = 0;
1909 vlink
(end_point
) = null
;
1910 vinfo
(end_point
+ 1) = null
;
1911 alink
(end_point
) = null
;
1912 font
(end_point
) = 0;
1913 character
(end_point
) = '.'
;
1914 vinfo
(end_point
+ 3) = 0;
1915 vlink
(end_point
+ 3) = 0;
1916 vinfo
(end_point
+ 4) = 0;
1917 vlink
(end_point
+ 4) = 0;
1921 void dump_node_mem
(void
)
1923 dump_int
(var_mem_max
);
1925 dump_things
(varmem
[0], var_mem_max
);
1926 #ifdef CHECK_NODE_USAGE
1927 dump_things
(varmem_sizes
[0], var_mem_max
);
1929 dump_things
(free_chain
[0], MAX_CHAIN_SIZE
);
1931 dump_int
(my_prealloc
);
1934 @ it makes sense to enlarge the varmem array immediately
1937 void undump_node_mem
(void
)
1942 var_mem_max
= (x
< 100000 ?
100000 : x
);
1943 varmem
= xmallocarray
(memory_word
, (unsigned
) var_mem_max
);
1944 undump_things
(varmem
[0], x
);
1945 #ifdef CHECK_NODE_USAGE
1946 varmem_sizes
= xmallocarray
(char
, (unsigned
) var_mem_max
);
1947 memset
((void
*) varmem_sizes
, 0, (unsigned
) var_mem_max
* sizeof
(char
));
1948 undump_things
(varmem_sizes
[0], x
);
1950 undump_things
(free_chain
[0], MAX_CHAIN_SIZE
);
1951 undump_int
(var_used
);
1952 undump_int
(my_prealloc
);
1953 if
(var_mem_max
> x
) {
1954 /* todo ? it is perhaps possible to merge the new node with an existing rover
*/
1956 node_size
(x
) = (var_mem_max
- x
);
1957 while
(vlink
(rover
) != vlink
(x
)) {
1958 rover
= vlink
(rover
);
1965 halfword slow_get_node
(int s
)
1970 t
= node_size
(rover
);
1971 if
(vlink
(rover
) < var_mem_max
&& vlink(rover) != 0) {
1973 /* allocating from the bottom helps decrease page faults
*/
1974 register halfword r
= rover
;
1976 vlink
(rover
) = vlink
(r
);
1977 node_size
(rover
) = node_size
(r
) - s
;
1978 if
(vlink
(rover
) != r
) { /* list is longer than one
*/
1980 while
(vlink
(q
) != r
) {
1987 if
(vlink
(rover
) < var_mem_max
) {
1988 #ifdef CHECK_NODE_USAGE
1989 varmem_sizes
[r
] = (char
) (s
> 127 ?
127 : s
);
1992 var_used
+= s
; /* maintain usage statistics
*/
1993 return r
; /* this is the only exit
*/
1995 normal_error
("nodes","there is a problem in getting a node, case 2");
1999 /* attempt to keep the free list small
*/
2001 if
(vlink
(rover
) != rover
) {
2002 if
(t
< MAX_CHAIN_SIZE
) {
2003 halfword l
= vlink
(rover
);
2004 vlink
(rover
) = free_chain
[t
];
2005 free_chain
[t
] = rover
;
2007 while
(vlink
(l
) != free_chain
[t
]) {
2014 while
(vlink
(rover
) != l
) {
2015 if
(node_size
(rover
) > s
) {
2018 rover
= vlink
(rover
);
2022 /* if we are still here
, it was apparently impossible to get a match
*/
2023 x
= (var_mem_max
>> 2) + s
;
2024 varmem
= (memory_word
*) realloc
((void
*) varmem
, sizeof
(memory_word
) * (unsigned
) (var_mem_max
+ x
));
2025 if
(varmem
== NULL) {
2026 overflow
("node memory size", (unsigned
) var_mem_max
);
2028 memset
((void
*) (varmem
+ var_mem_max
), 0, (unsigned
) x
* sizeof
(memory_word
));
2029 #ifdef CHECK_NODE_USAGE
2030 varmem_sizes
= (char
*) realloc
(varmem_sizes
, sizeof
(char
) * (unsigned
) (var_mem_max
+ x
));
2031 if
(varmem_sizes
== NULL) {
2032 overflow
("node memory size", (unsigned
) var_mem_max
);
2034 memset
((void
*) (varmem_sizes
+ var_mem_max
), 0, (unsigned
) (x
) * sizeof
(char
));
2036 /* todo ? it is perhaps possible to merge the new memory with an existing rover
*/
2037 vlink
(var_mem_max
) = rover
;
2038 node_size
(var_mem_max
) = x
;
2039 while
(vlink
(rover
) != vlink
(var_mem_max
)) {
2040 rover
= vlink
(rover
);
2042 vlink
(rover
) = var_mem_max
;
2043 rover
= var_mem_max
;
2048 normal_error
("nodes","there is a problem in getting a node, case 3");
2054 char
*sprint_node_mem_usage
(void
)
2057 #ifdef CHECK_NODE_USAGE
2062 int node_counts
[last_normal_node
+ last_whatsit_node
+ 2] = { 0 };
2064 for
(i
= (var_mem_max
- 1); i
> my_prealloc
; i--
) {
2065 if
(varmem_sizes
[i
] > 0) {
2066 if
(type
(i
) > last_normal_node
+ last_whatsit_node
) {
2067 node_counts
[last_normal_node
+ last_whatsit_node
+ 1]++;
2068 } else if
(type
(i
) == whatsit_node
) {
2069 node_counts
[(subtype
(i
) + last_normal_node
+ 1)]++;
2071 node_counts
[type
(i
)]++;
2075 for
(i
= 0; i
< last_normal_node
+ last_whatsit_node
+ 2; i
++) {
2076 if
(node_counts
[i
] > 0) {
2078 (i
> (last_normal_node
+ 1) ?
(i
- last_normal_node
- 1) : 0);
2079 snprintf
(msg
, 255, "%s%d %s", (b ?
", " : ""), (int
) node_counts
[i
],
2080 get_node_name
((i
> last_normal_node ? whatsit_node
: i
), j
));
2081 ss
= xmalloc
((unsigned
) (strlen
(s
) + strlen
(msg
) + 1));
2096 halfword list_node_mem_usage
(void
)
2099 #ifdef CHECK_NODE_USAGE
2102 char
*saved_varmem_sizes
= xmallocarray
(char
, (unsigned
) var_mem_max
);
2103 memcpy
(saved_varmem_sizes
, varmem_sizes
, (size_t
) var_mem_max
);
2104 for
(i
= my_prealloc
+ 1; i
< (var_mem_max
- 1); i
++) {
2105 if
(saved_varmem_sizes
[i
] > 0) {
2115 free
(saved_varmem_sizes
);
2121 void print_node_mem_stats
(void
)
2127 int free_chain_counts
[MAX_CHAIN_SIZE
] = { 0 };
2128 snprintf
(msg
, 255, " %d words of node memory still in use:", (int
) (var_used
+ my_prealloc
));
2130 s
= sprint_node_mem_usage
();
2135 tprint_nl
(" avail lists: ");
2137 for
(i
= 1; i
< MAX_CHAIN_SIZE
; i
++) {
2138 for
(j
= free_chain
[i
]; j
!= null
; j
= vlink
(j
))
2139 free_chain_counts
[i
]++;
2140 if
(free_chain_counts
[i
] > 0) {
2141 snprintf
(msg
, 255, "%s%d:%d", (b ?
"," : ""), i
, (int
) free_chain_counts
[i
]);
2146 /* newline
, if needed
*/
2150 /* this belongs in the web but i couldn't find the correct syntactic place
*/
2152 halfword new_span_node
(halfword n
, int s
, scaled w
)
2154 halfword p
= new_node
(span_node
, 0);
2164 static halfword new_attribute_node
(unsigned int i
, int v
)
2166 register halfword r
= get_node
(attribute_node_size
);
2167 type
(r
) = attribute_node
;
2168 attribute_id
(r
) = (halfword
) i
;
2169 attribute_value
(r
) = v
;
2170 /* not used but nicer in print
*/
2176 halfword copy_attribute_list
(halfword n
)
2178 halfword q
= get_node
(attribute_node_size
);
2179 register halfword p
= q
;
2180 type
(p
) = attribute_list_node
;
2181 attr_list_ref
(p
) = 0;
2184 register halfword r
= get_node
(attribute_node_size
);
2185 /* the link will be fixed automatically in the next loop
*/
2186 (void
) memcpy
((void
*) (varmem
+ r
), (void
*) (varmem
+ n
),
2187 (sizeof
(memory_word
) * attribute_node_size
));
2196 void update_attribute_cache
(void
)
2200 attr_list_cache
= get_node
(attribute_node_size
);
2201 type
(attr_list_cache
) = attribute_list_node
;
2202 attr_list_ref
(attr_list_cache
) = 0;
2203 p
= attr_list_cache
;
2204 for
(i
= 0; i
<= max_used_attr
; i
++) {
2205 register int v
= attribute
(i
);
2206 if
(v
> UNUSED_ATTRIBUTE
) {
2207 register halfword r
= new_attribute_node
((unsigned
) i
, v
);
2212 if
(vlink
(attr_list_cache
) == null
) {
2213 free_node
(attr_list_cache
, attribute_node_size
);
2214 attr_list_cache
= null
;
2220 void build_attribute_list
(halfword b
)
2222 if
(max_used_attr
>= 0) {
2223 if
(attr_list_cache
== cache_disabled|| attr_list_cache
== null
) {
2224 update_attribute_cache
();
2225 if
(attr_list_cache
== null
)
2228 attr_list_ref
(attr_list_cache
)++;
2229 node_attr
(b
) = attr_list_cache
;
2234 halfword current_attribute_list
(void
)
2236 if
(max_used_attr
>= 0) {
2237 if
(attr_list_cache
== cache_disabled
) {
2238 update_attribute_cache
();
2240 return attr_list_cache
;
2247 void reassign_attribute
(halfword n
, halfword new
)
2252 /* there is nothing to assign but we need to check for an old value
*/
2254 delete_attribute_ref
(old
); /* also nulls attr field of n
*/
2255 } else if
(old
== null
) {
2256 /* nothing is assigned so we just do that now
*/
2257 assign_attribute_ref
(n
,new
);
2258 } else if
(old
!= new
) {
2259 /* something is assigned so we need to clean up and assign then
*/
2260 delete_attribute_ref
(old
);
2261 assign_attribute_ref
(n
,new
);
2263 /* else
: same value so there is no need to assign and change the refcount
*/
2264 node_attr
(n
) = new
;
2268 void delete_attribute_ref
(halfword b
)
2271 if
(type
(b
) == attribute_list_node
){
2273 if
(attr_list_ref
(b
) == 0) {
2274 if
(b
== attr_list_cache
)
2275 attr_list_cache
= cache_disabled
;
2276 free_node_chain
(b
, attribute_node_size
);
2278 /* maintain sanity
*/
2279 if
(attr_list_ref
(b
) < 0) {
2280 attr_list_ref
(b
) = 0;
2283 normal_error
("nodes","trying to delete an attribute reference of a non attribute node");
2288 void reset_node_properties
(halfword b
)
2291 lua_properties_reset
(b
);
2295 @ |p| is an attr list head
, or zero
2297 halfword do_set_attribute
(halfword p
, int i
, int val
)
2299 register halfword q
;
2301 if
(p
== null
) { /* add a new head \
& node */
2302 q
= get_node
(attribute_node_size
);
2303 type
(q
) = attribute_list_node
;
2304 attr_list_ref
(q
) = 1;
2305 p
= new_attribute_node
((unsigned
) i
, val
);
2310 if
(vlink
(p
) != null
) {
2311 while
(vlink
(p
) != null
) {
2312 int t
= attribute_id
(vlink
(p
));
2313 if
(t
== i
&& attribute_value(vlink(p)) == val)
2314 return q
; /* no need to do anything
*/
2324 if
(attribute_id
(vlink
(p
)) == i
) {
2325 attribute_value
(vlink
(p
)) = val
;
2326 } else
{ /* add a new node
*/
2327 halfword r
= new_attribute_node
((unsigned
) i
, val
);
2328 vlink
(r
) = vlink
(p
);
2333 normal_error
("nodes","trying to set an attribute fails, case 1");
2339 void set_attribute
(halfword n
, int i
, int val
)
2341 register halfword p
;
2343 /* not all nodes can have an attribute list
*/
2344 if
(!nodetype_has_attributes
(type
(n
)))
2346 /* if we have no list
, we create one and quit
*/
2348 if
(p
== null
) { /* add a new head \
& node */
2349 p
= get_node
(attribute_node_size
);
2350 type
(p
) = attribute_list_node
;
2351 attr_list_ref
(p
) = 1;
2353 p
= new_attribute_node
((unsigned
) i
, val
);
2354 vlink
(node_attr
(n
)) = p
;
2357 /* we check if we have this attribute already and quit if the value stays the same
*/
2358 if
(vlink
(p
) != null
) {
2359 while
(vlink
(p
) != null
) {
2360 int t
= attribute_id
(vlink
(p
));
2361 if
(t
== i
&& attribute_value(vlink(p)) == val)
2368 /* j has now the position
(if found
) .. we assume a sorted list
! */
2371 if
(attr_list_ref
(p
) == 0 ) {
2372 /* the list is invalid i.e. freed already
*/
2373 formatted_warning
("nodes","node %d has an attribute list that is free already, case 1",(int
) n
);
2374 /* the still dangling list gets ref count
1 */
2375 attr_list_ref
(p
) = 1;
2376 } else if
(attr_list_ref
(p
) == 1) {
2377 /* this can really happen HH-LS
*/
2378 if
(p
== attr_list_cache
) {
2379 /* we can invalidate the cache setting
*/
2380 /* attr_list_cache
= cache_disabled
*/
2381 /* or save the list
, as done below
*/
2382 p
= copy_attribute_list
(p
);
2384 /* the copied list gets ref count
1 */
2385 attr_list_ref
(p
) = 1;
2388 /* the list is used multiple times so we make a copy
*/
2389 p
= copy_attribute_list
(p
);
2390 /* we decrement the ref count or the original
*/
2391 delete_attribute_ref
(node_attr
(n
));
2393 /* the copied list gets ref count
1 */
2394 attr_list_ref
(p
) = 1;
2398 /* we go to position j in the list
*/
2401 /* if we have a hit we just set the value otherwise we add a new node
*/
2402 if
(attribute_id
(vlink
(p
)) == i
) {
2403 attribute_value
(vlink
(p
)) = val
;
2404 } else
{ /* add a new node
*/
2405 halfword r
= new_attribute_node
((unsigned
) i
, val
);
2406 vlink
(r
) = vlink
(p
);
2410 normal_error
("nodes","trying to set an attribute fails, case 2");
2415 int unset_attribute
(halfword n
, int i
, int val
)
2417 register halfword p
;
2421 if
(!nodetype_has_attributes
(type
(n
)))
2425 return UNUSED_ATTRIBUTE
;
2426 if
(attr_list_ref
(p
) == 0) {
2427 formatted_warning
("nodes","node %d has an attribute list that is free already, case 2", (int
) n
);
2428 return UNUSED_ATTRIBUTE
;
2430 if
(vlink
(p
) != null
) {
2431 while
(vlink
(p
) != null
) {
2432 t
= attribute_id
(vlink
(p
));
2434 return UNUSED_ATTRIBUTE
;
2442 if
(attribute_id
(p
) != i
)
2443 return UNUSED_ATTRIBUTE
;
2444 /* if we are still here
, the attribute exists
*/
2446 if
(attr_list_ref
(p
) > 1 || p
== attr_list_cache
) {
2447 halfword q
= copy_attribute_list
(p
);
2448 if
(attr_list_ref
(p
) > 1) {
2449 delete_attribute_ref
(node_attr
(n
));
2451 attr_list_ref
(q
) = 1;
2454 p
= vlink
(node_attr
(n
));
2457 t
= attribute_value
(p
);
2458 if
(val
== UNUSED_ATTRIBUTE || t
== val
) {
2459 attribute_value
(p
) = UNUSED_ATTRIBUTE
;
2463 normal_error
("nodes","trying to unset an attribute fails");
2469 int has_attribute
(halfword n
, int i
, int val
)
2471 register halfword p
;
2472 if
(!nodetype_has_attributes
(type
(n
)))
2473 return UNUSED_ATTRIBUTE
;
2475 if
(p
== null || vlink
(p
) == null
)
2476 return UNUSED_ATTRIBUTE
;
2479 if
(attribute_id
(p
) == i
) {
2480 int ret
= attribute_value
(p
);
2481 if
(val
== UNUSED_ATTRIBUTE || val
== ret
)
2483 return UNUSED_ATTRIBUTE
;
2484 } else if
(attribute_id
(p
) > i
) {
2485 return UNUSED_ATTRIBUTE
;
2489 return UNUSED_ATTRIBUTE
;
2493 void print_short_node_contents
(halfword p
)
2510 if
(glue_ptr
(p
) != zero_glue
)
2517 short_display
(vlink
(pre_break
(p
)));
2518 short_display
(vlink
(post_break
(p
)));
2524 static void show_pdftex_whatsit_rule_spec
(int p
)
2527 print_rule_dimen
(height
(p
));
2529 print_rule_dimen
(depth
(p
));
2531 print_rule_dimen
(width
(p
));
2534 @ Each new type of node that appears in our data structure must be capable
2535 of being displayed
, copied
, destroyed
, and so on. The routines that we
2536 need for write-oriented whatsits are somewhat like those for mark nodes
;
2537 other extensions might
, of course
, involve more subtlety here.
2540 static void print_write_whatsit
(const char
*s
, pointer p
)
2543 if
(write_stream
(p
) < 16)
2544 print_int
(write_stream
(p
));
2545 else if
(write_stream
(p
) == 16)
2552 static void show_node_wrapup_core
(int p
)
2554 switch
(subtype
(p
)) {
2556 print_write_whatsit
("openout", p
);
2558 print_file_name
(open_name
(p
), open_area
(p
), open_ext
(p
));
2561 print_write_whatsit
("write", p
);
2562 print_mark
(write_tokens
(p
));
2565 print_write_whatsit
("closeout", p
);
2568 tprint_esc
("special");
2569 print_mark
(write_tokens
(p
));
2575 tprint_esc
("savepos");
2577 case user_defined_node
:
2578 tprint_esc
("whatsit");
2579 print_int
(user_node_id
(p
));
2581 switch
(user_node_type
(p
)) {
2587 show_node_list
(user_node_value
(p
));
2592 print(user_node_value(p));
2596 print_mark
(user_node_value
(p
));
2598 default
: /* only 'd'
*/
2599 print_int
(user_node_value
(p
));
2606 void show_node_wrapup_dvi
(int p
)
2610 void show_node_wrapup_pdf
(int p
)
2612 switch
(subtype
(p
)) {
2613 case pdf_literal_node
:
2614 show_pdf_literal
(p
);
2616 case pdf_colorstack_node
:
2617 tprint_esc
("pdfcolorstack ");
2618 print_int
(pdf_colorstack_stack
(p
));
2619 switch
(pdf_colorstack_cmd
(p
)) {
2620 case colorstack_set
:
2623 case colorstack_push
:
2626 case colorstack_pop
:
2629 case colorstack_current
:
2633 confusion
("colorstack");
2636 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
2637 print_mark
(pdf_colorstack_data
(p
));
2639 case pdf_setmatrix_node
:
2640 tprint_esc
("pdfsetmatrix");
2641 print_mark
(pdf_setmatrix_data
(p
));
2644 tprint_esc
("pdfsave");
2646 case pdf_restore_node
:
2647 tprint_esc
("pdfrestore");
2649 case pdf_refobj_node
:
2650 tprint_esc
("pdfrefobj");
2651 if
(obj_obj_is_stream
(static_pdf
, pdf_obj_objnum
(p
))) {
2652 if
(obj_obj_stream_attr
(static_pdf
, pdf_obj_objnum
(p
)) != LUA_NOREF
) {
2654 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
,
2655 obj_obj_stream_attr
(static_pdf
, pdf_obj_objnum
(p
)));
2657 tprint
((const char
*) lua_tostring
(Luas
, -1));
2662 if
(obj_obj_is_file
(static_pdf
, pdf_obj_objnum
(p
)))
2664 if
(obj_obj_data
(static_pdf
, pdf_obj_objnum
(p
)) != LUA_NOREF
) {
2665 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
,
2666 obj_obj_data
(static_pdf
, pdf_obj_objnum
(p
)));
2668 tprint
((const char
*) lua_tostring
(Luas
, -1));
2672 case pdf_annot_node
:
2673 tprint_esc
("pdfannot");
2674 show_pdftex_whatsit_rule_spec
(p
);
2675 print_mark
(pdf_annot_data
(p
));
2677 case pdf_start_link_node
:
2678 tprint_esc
("pdfstartlink");
2679 show_pdftex_whatsit_rule_spec
(p
);
2680 if
(pdf_link_attr
(p
) != null
) {
2682 print_mark
(pdf_link_attr
(p
));
2685 if
(pdf_action_type
(pdf_link_action
(p
)) == pdf_action_user
) {
2687 print_mark
(pdf_action_tokens
(pdf_link_action
(p
)));
2690 if
(pdf_action_file
(pdf_link_action
(p
)) != null
) {
2692 print_mark
(pdf_action_file
(pdf_link_action
(p
)));
2694 switch
(pdf_action_type
(pdf_link_action
(p
))) {
2695 case pdf_action_goto
:
2696 if
(pdf_action_named_id
(pdf_link_action
(p
)) > 0) {
2697 tprint
(" goto name");
2698 print_mark
(pdf_action_id
(pdf_link_action
(p
)));
2700 tprint
(" goto num");
2701 print_int
(pdf_action_id
(pdf_link_action
(p
)));
2704 case pdf_action_page
:
2706 print_int
(pdf_action_id
(pdf_link_action
(p
)));
2707 print_mark
(pdf_action_tokens
(pdf_link_action
(p
)));
2709 case pdf_action_thread
:
2710 if
(pdf_action_named_id
(pdf_link_action
(p
)) > 0) {
2711 tprint
(" thread name");
2712 print_mark
(pdf_action_id
(pdf_link_action
(p
)));
2714 tprint
(" thread num");
2715 print_int
(pdf_action_id
(pdf_link_action
(p
)));
2719 normal_error
("pdf backend", "unknown action type for link");
2723 case pdf_end_link_node
:
2724 tprint_esc
("pdfendlink");
2727 tprint_esc
("pdfdest");
2728 if
(pdf_dest_named_id
(p
) > 0) {
2730 print_mark
(pdf_dest_id
(p
));
2733 print_int
(pdf_dest_id
(p
));
2736 switch
(pdf_dest_type
(p
)) {
2739 if
(pdf_dest_xyz_zoom
(p
) != null
) {
2741 print_int
(pdf_dest_xyz_zoom
(p
));
2744 case pdf_dest_fitbh
:
2747 case pdf_dest_fitbv
:
2761 show_pdftex_whatsit_rule_spec
(p
);
2771 case pdf_thread_node
:
2772 case pdf_start_thread_node
:
2773 if
(subtype
(p
) == pdf_thread_node
)
2774 tprint_esc
("pdfthread");
2776 tprint_esc
("pdfstartthread");
2778 print_rule_dimen
(height
(p
));
2780 print_rule_dimen
(depth
(p
));
2782 print_rule_dimen
(width
(p
));
2783 if
(pdf_thread_attr
(p
) != null
) {
2785 print_mark
(pdf_thread_attr
(p
));
2787 if
(pdf_thread_named_id
(p
) > 0) {
2789 print_mark
(pdf_thread_id
(p
));
2792 print_int
(pdf_thread_id
(p
));
2795 case pdf_end_thread_node
:
2796 tprint_esc
("pdfendthread");
2803 @ Now we are ready for |show_node_list| itself. This procedure has been
2804 written to be ``extra robust'' in the sense that it should not crash or get
2805 into a loop even if the data structures have been messed up by bugs in
2806 the rest of the program. You can safely call its parent routine
2807 |show_box
(p
)| for arbitrary values of |p| when you are debugging \TeX.
2808 However
, in the presence of bad data
, the procedure may
2809 fetch a |memory_word| whose variant is different from the way it was stored
;
2810 for example
, it might try to read |mem
[p
].hh| when |mem
[p
]|
2811 contains a scaled integer
, if |p| is a pointer that has been
2812 clobbered or chosen at random.
2815 @ |str_room| need not be checked
; see |show_box|
2817 @ Recursive calls on |show_node_list| therefore use the following pattern
:
2819 #define node_list_display
(A
) do
{ \
2821 show_node_list
(A
); \
2825 /* prints a node list symbolically
*/
2827 void show_node_list
(int p
)
2829 int n
= 0; /* the number of items already printed at this level
*/
2831 real g
; /* a glue ratio
, as a floating point number
*/
2832 if
((int
) cur_length
> depth_threshold
) {
2834 tprint
(" []"); /* indicate that there's been some truncation
*/
2839 print_current_string
(); /* display the nesting history
*/
2840 if
(int_par
(tracing_online_code
) < -2)
2843 if
(n
> breadth_max
) { /* time to stop
*/
2847 /* Display node |p|
*/
2848 if
(is_char_node
(p
)) {
2849 print_font_and_char
(p
);
2850 if
(is_ligature
(p
)) {
2851 /* Display ligature |p|
; */
2852 tprint
(" (ligature ");
2853 if
(is_leftboundary
(p
))
2855 font_in_short_display
= font
(p
);
2856 short_display
(lig_ptr
(p
));
2857 if
(is_rightboundary
(p
))
2866 /* Display box |p|
; */
2867 if
(type
(p
) == hlist_node
)
2869 else if
(type
(p
) == vlist_node
)
2872 tprint_esc
("unset");
2874 print_scaled
(height
(p
));
2876 print_scaled
(depth
(p
));
2878 print_scaled
(width
(p
));
2879 if
(type
(p
) == unset_node
) {
2880 /* Display special fields of the unset node |p|
; */
2881 if
(span_count
(p
) != min_quarterword
) {
2883 print_int
(span_count
(p
) + 1);
2884 tprint
(" columns)");
2886 if
(glue_stretch
(p
) != 0) {
2887 tprint
(", stretch ");
2888 print_glue
(glue_stretch
(p
), glue_order
(p
), NULL);
2890 if
(glue_shrink
(p
) != 0) {
2891 tprint
(", shrink ");
2892 print_glue
(glue_shrink
(p
), glue_sign
(p
), NULL);
2895 /* Display the value of |glue_set
(p
)|
*/
2896 /* The code will have to change in this place if |glue_ratio| is
2897 a structured type instead of an ordinary |real|. Note that this routine
2898 should avoid arithmetic errors even if the |glue_set| field holds an
2899 arbitrary random value. The following code assumes that a properly
2900 formed nonzero |real| number has absolute value $
2^
{20}$ or more when
2901 it is regarded as an integer
; this precaution was adequate to prevent
2902 floating point underflow on the author's computer.
2905 g
= (real
) (glue_set
(p
));
2906 if
((g
!= 0.0) && (glue_sign(p) != normal)) {
2907 tprint
(", glue set ");
2908 if
(glue_sign
(p
) == shrinking
)
2910 if
(g
> 20000.0 || g
< -20000.0) {
2915 print_glue
(20000 * unity
, glue_order
(p
), NULL);
2917 print_glue
(round
(unity
* g
), glue_order
(p
), NULL);
2921 if
(shift_amount
(p
) != 0) {
2922 tprint
(", shifted ");
2923 print_scaled
(shift_amount
(p
));
2925 tprint
(", direction ");
2926 print_dir
(box_dir
(p
));
2928 node_list_display
(list_ptr
(p
)); /* recursive call
*/
2931 /* Display rule |p|
; */
2932 if
(subtype
(p
) == normal_rule
) {
2933 tprint_esc
("rule(");
2934 } else if
(subtype
(p
) == empty_rule
) {
2935 tprint_esc
("norule(");
2936 } else if
(subtype
(p
) == user_rule
) {
2937 tprint_esc
("userrule(");
2938 } else if
(subtype
(p
) == box_rule
) {
2940 } else if
(subtype
(p
) == image_rule
) {
2941 tprint_esc
("image(");
2943 print_rule_dimen
(height
(p
));
2945 print_rule_dimen
(depth
(p
));
2947 print_rule_dimen
(width
(p
));
2950 /* Display insertion |p|
; */
2951 tprint_esc
("insert");
2952 print_int
(subtype
(p
));
2953 tprint
(", natural size ");
2954 print_scaled
(height
(p
));
2956 print_spec
(split_top_ptr
(p
), NULL);
2958 print_scaled
(depth
(p
));
2959 tprint
("); float cost ");
2960 print_int
(float_cost
(p
));
2961 node_list_display
(ins_ptr
(p
)); /* recursive call
*/
2964 if
(dir_dir
(p
) < 0) {
2965 tprint_esc
("enddir");
2967 print_dir
(dir_dir
(p
) + dir_swap
);
2969 tprint_esc
("begindir");
2971 print_dir
(dir_dir
(p
));
2974 case local_par_node
:
2975 tprint_esc
("localpar");
2978 print_current_string
();
2979 tprint_esc
("localinterlinepenalty");
2981 print_int
(local_pen_inter
(p
));
2983 print_current_string
();
2984 tprint_esc
("localbrokenpenalty");
2986 print_int
(local_pen_broken
(p
));
2988 print_current_string
();
2989 tprint_esc
("localleftbox");
2990 if
(local_box_left
(p
) == null
) {
2994 show_node_list
(local_box_left
(p
));
2998 print_current_string
();
2999 tprint_esc
("localrightbox");
3000 if
(local_box_right
(p
) == null
) {
3004 show_node_list
(local_box_right
(p
));
3010 if
(subtype
(p
)==0) {
3011 tprint_esc
("noboundary");
3013 tprint_esc
("boundary");
3015 print_int
(subtype
(p
));
3017 print_int
(boundary_value
(p
));
3022 if
(w
>= backend_first_pdf_whatsit
) {
3023 show_node_wrapup_pdf
(p
);
3024 } else if
(w
>= backend_first_dvi_whatsit
) {
3025 show_node_wrapup_dvi
(p
);
3027 show_node_wrapup_core
(p
);
3031 /* Display glue |p|
; */
3032 if
(subtype
(p
) >= a_leaders
) {
3033 /* Display leaders |p|
; */
3035 switch
(subtype
(p
)) {
3048 normal_warning
("nodes","weird glue leader subtype ignored");
3051 print_spec
(glue_ptr
(p
), NULL);
3052 node_list_display
(leader_ptr
(p
)); /* recursive call
*/
3055 if
(subtype
(p
) != normal
) {
3057 if
((subtype
(p
) - 1) < thin_mu_skip_code
) {
3058 print_cmd_chr
(assign_glue_cmd
,
3059 glue_base
+ (subtype
(p
) - 1));
3060 } else if
(subtype
(p
) < cond_math_glue
) {
3061 print_cmd_chr
(assign_mu_glue_cmd
,
3062 glue_base
+ (subtype
(p
) - 1));
3063 } else if
(subtype
(p
) == cond_math_glue
) {
3064 tprint_esc
("nonscript");
3066 tprint_esc
("mskip");
3070 if
(subtype
(p
) != cond_math_glue
) {
3072 if
(subtype
(p
) < cond_math_glue
)
3073 print_spec
(glue_ptr
(p
), NULL);
3075 print_spec
(glue_ptr
(p
), "mu");
3079 case margin_kern_node
:
3081 print_scaled
(width
(p
));
3082 if
(subtype
(p
) == left_side
)
3083 tprint
(" (left margin)");
3085 tprint
(" (right margin)");
3088 /* Display kern |p|
; */
3089 /* An ``explicit'' kern value is indicated implicitly by an explicit space.
*/
3090 if
(subtype
(p
) != mu_glue
) {
3092 if
(subtype
(p
) != normal
)
3094 print_scaled
(width
(p
));
3095 if
(subtype
(p
) == accent_kern
)
3096 tprint
(" (for accent)");
3098 tprint_esc
("mkern");
3099 print_scaled
(width
(p
));
3104 /* Display math node |p|
; */
3106 if
(subtype
(p
) == before
)
3110 if
(width
(p
) != 0) {
3111 tprint
(", surrounded ");
3112 print_scaled
(width
(p
));
3116 /* Display penalty |p|
; */
3117 tprint_esc
("penalty ");
3118 print_int
(penalty
(p
));
3121 /* Display discretionary |p|
; */
3122 /* The |post_break| list of a discretionary node is indicated by a prefixed
3123 `\.
{\char'
174}' instead of the `\..' before the |pre_break| list.
*/
3124 tprint_esc
("discretionary");
3125 print_int
(disc_penalty
(p
));
3127 if
(vlink
(no_break
(p
)) != null
) {
3128 tprint
(" replacing ");
3129 node_list_display
(vlink
(no_break
(p
)));
3131 node_list_display
(vlink
(pre_break
(p
))); /* recursive call
*/
3133 show_node_list
(vlink
(post_break
(p
)));
3134 flush_char
(); /* recursive call
*/
3137 /* Display mark |p|
; */
3139 if
(mark_class
(p
) != 0) {
3141 print_int
(mark_class
(p
));
3143 print_mark
(mark_ptr
(p
));
3146 /* Display adjustment |p|
; */
3147 tprint_esc
("vadjust");
3148 if
(subtype
(p
) != 0)
3150 node_list_display
(adjust_ptr
(p
)); /* recursive call
*/
3152 case glue_spec_node
:
3153 tprint
("<glue_spec ");
3154 print_spec
(p
, NULL);
3166 @ This routine finds the 'base' width of a horizontal box
, using the same logic
3167 that \TeX82 used for \.
{\\predisplaywidth
} */
3170 pointer actual_box_width
(pointer r
, scaled base_width
)
3172 pointer q
; /* glue specification when calculating |pre_display_size|
*/
3173 scaled d
; /* increment to |v|
*/
3174 scaled w
= -max_dimen
; /* calculated |size|
*/
3175 scaled v
= shift_amount
(r
) + base_width
; /* |w| plus possible glue amount
*/
3176 pointer p
= list_ptr
(r
); /* current node when calculating |pre_display_size|
*/
3178 if
(is_char_node
(p
)) {
3189 case margin_kern_node
:
3196 /* begin mathskip code
*/
3197 if
(glue_ptr
(p
) == zero_glue
) {
3203 /* end mathskip code
*/
3205 /* We need to be careful that |w|
, |v|
, and |d| do not depend on any |glue_set|
3206 values
, since such values are subject to system-dependent rounding.
3207 System-dependent numbers are not allowed to infiltrate parameters like
3208 |pre_display_size|
, since \TeX82 is supposed to make the same decisions on all
3213 if
(glue_sign
(r
) == stretching
) {
3214 if
((glue_order
(r
) == stretch_order
(q
))
3215 && (stretch(q) != 0))
3217 } else if
(glue_sign
(r
) == shrinking
) {
3218 if
((glue_order
(r
) == shrink_order
(q
))
3219 && (shrink(q) != 0))
3222 if
(subtype
(p
) >= a_leaders
)
3233 if
(v
< max_dimen
) {
3247 halfword tail_of_list
(halfword p
)
3250 while
(vlink
(q
) != null
)
3255 @ |delete_glue_ref| is called when a pointer to a glue
3256 specification is being withdrawn.
3259 void delete_glue_ref
(halfword p
)
3260 { /* |p| points to a glue specification
*/
3261 if
(type
(p
) == glue_spec_node
) {
3262 if
(glue_ref_count
(p
) == null
) {
3265 decr
(glue_ref_count
(p
));
3268 normal_error
("nodes","invalid glue spec node");
3274 halfword temp_ptr
; /* a pointer variable for occasional emergency use
*/
3276 @ Attribute lists need two extra globals to increase processing efficiency.
3277 |max_used_attr| limits the test loop that checks for set attributes
, and
3278 |attr_list_cache| contains a pointer to an already created attribute list. It is
3279 set to the special value |cache_disabled| when the current value can no longer be
3280 trusted
: after an assignment to an attribute register
, and after a group has
3284 int max_used_attr
; /* maximum assigned attribute id
*/
3285 halfword attr_list_cache
;
3287 @ From the computer's standpoint
, \TeX's chief mission is to create
3288 horizontal and vertical lists. We shall now investigate how the elements
3289 of these lists are represented internally as nodes in the dynamic memory.
3291 A horizontal or vertical list is linked together by |link| fields in
3292 the first word of each node. Individual nodes represent boxes
, glue
,
3293 penalties
, or special things like discretionary hyphens
; because of this
3294 variety
, some nodes are longer than others
, and we must distinguish different
3295 kinds of nodes. We do this by putting a `|type|' field in the first word
,
3296 together with the link and an optional `|subtype|'.
3298 @ Character nodes appear only in horizontal lists
, never in vertical lists.
3300 An |hlist_node| stands for a box that was made from a horizontal list.
3301 Each |hlist_node| is seven words long
, and contains the following fields
3302 (in addition to the mandatory |type| and |link|
, which we shall not
3303 mention explicitly when discussing the other node types
): The |height| and
3304 |width| and |depth| are scaled integers denoting the dimensions of the
3305 box. There is also a |shift_amount| field
, a scaled integer indicating
3306 how much this box should be lowered
(if it appears in a horizontal list
),
3307 or how much it should be moved to the right
(if it appears in a vertical
3308 list
). There is a |list_ptr| field
, which points to the beginning of the
3309 list from which this box was fabricated
; if |list_ptr| is |null|
, the box
3310 is empty. Finally
, there are three fields that represent the setting of
3311 the glue
: |glue_set
(p
)| is a word of type |glue_ratio| that represents
3312 the proportionality constant for glue setting
; |glue_sign
(p
)| is
3313 |stretching| or |shrinking| or |normal| depending on whether or not the
3314 glue should stretch or shrink or remain rigid
; and |glue_order
(p
)|
3315 specifies the order of infinity to which glue setting applies
(|normal|
,
3316 |sfi|
, |fil|
, |fill|
, or |filll|
). The |subtype| field is not used.
3318 @ The |new_null_box| function returns a pointer to an |hlist_node| in
3319 which all subfields have the values corresponding to `\.
{\\hbox\
{\
}}'.
3320 The |subtype| field is set to |min_quarterword|
, since that's the desired
3321 |span_count| value if this |hlist_node| is changed to an |unset_node|.
3324 halfword new_null_box
(void
)
3325 { /* creates a new box node
*/
3326 halfword p
= new_node
(hlist_node
, min_quarterword
);
3327 box_dir
(p
) = text_direction
;
3331 @ A |vlist_node| is like an |hlist_node| in all respects except that it
3332 contains a vertical list.
3334 @ A |rule_node| stands for a solid black rectangle
; it has |width|
,
3335 |depth|
, and |height| fields just as in an |hlist_node|. However
, if
3336 any of these dimensions is $
-2^
{30}$
, the actual value will be determined
3337 by running the rule up to the boundary of the innermost enclosing box.
3338 This is called a ``running dimension.'' The |width| is never running in
3339 an hlist
; the |height| and |depth| are never running in a~vlist.
3341 @ A new rule node is delivered by the |new_rule| function. It
3342 makes all the dimensions ``running
,'' so you have to change the
3343 ones that are not allowed to run.
3346 halfword new_rule
(int s
)
3348 halfword p
= new_node
(rule_node
,s
);
3352 @ Insertions are represented by |ins_node| records
, where the |subtype|
3353 indicates the corresponding box number. For example
, `\.
{\\insert
250}'
3354 leads to an |ins_node| whose |subtype| is |
250+min_quarterword|.
3355 The |height| field of an |ins_node| is slightly misnamed
; it actually holds
3356 the natural height plus depth of the vertical list being inserted.
3357 The |depth| field holds the |split_max_depth| to be used in case this
3358 insertion is split
, and the |split_top_ptr| points to the corresponding
3359 |split_top_skip|. The |float_cost| field holds the |floating_penalty| that
3360 will be used if this insertion floats to a subsequent page after a
3361 split insertion of the same class. There is one more field
, the
3362 |ins_ptr|
, which points to the beginning of the vlist for the insertion.
3364 @ A |mark_node| has a |mark_ptr| field that points to the reference count
3365 of a token list that contains the user's \.
{\\mark
} text.
3366 In addition there is a |mark_class| field that contains the mark class.
3368 @ An |adjust_node|
, which occurs only in horizontal lists
,
3369 specifies material that will be moved out into the surrounding
3370 vertical list
; i.e.
, it is used to implement \TeX's `\.
{\\vadjust
}'
3371 operation. The |adjust_ptr| field points to the vlist containing this
3374 @ A |glyph_node|
, which occurs only in horizontal lists
, specifies a
3375 glyph in a particular font
, along with its attribute list. Older
3376 versions of \TeX\ could use token memory for characters
, because the
3377 font
,char combination would fit in a single word
(both values were
3378 required to be strictly less than $
2^
{16}$
). In LuaTeX
, room is
3379 needed for characters that are larger than that
, as well as a pointer
3380 to a potential attribute list
, and the two displacement values.
3382 In turn
, that made the node so large that it made sense to merge
3383 ligature glyphs as well
, as that requires only one extra pointer. A
3384 few extra classes of glyph nodes will be introduced later. The
3385 unification of all those types makes it easier to manipulate lists of
3386 glyphs. The subtype differentiates various glyph kinds.
3388 First
, here is a function that returns a pointer to a glyph node for a given
3389 glyph in a given font. If that glyph doesn't exist
, |null| is returned
3390 instead. Nodes of this subtype are directly created only for accents
3391 and their base
(through |make_accent|
), and math nucleus items
(in the
3392 conversion from |mlist| to |hlist|
).
3395 halfword new_glyph
(int f
, int c
)
3397 halfword p
= null
; /* the new node
*/
3398 if
((f
== 0) ||
(char_exists
(f
, c
))) {
3399 p
= new_glyph_node
();
3407 @ A subset of the glyphs nodes represent ligatures
: characters
3408 fabricated from the interaction of two or more actual characters. The
3409 characters that generated the ligature have not been forgotten
, since
3410 they are needed for diagnostic messages
; the |lig_ptr| field points to
3411 a linked list of character nodes for all original characters that have
3412 been deleted.
(This list might be empty if the characters that
3413 generated the ligature were retained in other nodes.
)
3415 The |subtype| field of these |glyph_node|s is
1, plus
2 and
/or
1 if
3416 the original source of the ligature included implicit left and
/or
3417 right boundaries. These nodes are created by the C function |new_ligkern|.
3419 A third general type of glyphs could be called a character
, as it
3420 only appears in lists that are not yet processed by the ligaturing and
3421 kerning steps of the program.
3423 |main_control| inserts these
, and they are later converted to
3424 |subtype_normal| by |new_ligkern|.
3427 quarterword norm_min
(int h
)
3434 return
(quarterword
) h
;
3437 halfword new_char
(int f
, int c
)
3439 halfword p
; /* the new node
*/
3440 p
= new_glyph_node
();
3441 set_to_character
(p
);
3444 lang_data
(p
) = make_lang_data
(uc_hyph
, cur_lang
, left_hyphen_min
, right_hyphen_min
);
3448 @ Left and right ghost glyph nodes are the result of \.
{\\leftghost
}
3449 and \.
{\\rightghost
}, respectively. They are going to be removed by
3450 |new_ligkern|
, at the end of which they are no longer needed.
3452 @ Here are a few handy helpers used by the list output routines.
3455 scaled glyph_width
(halfword p
)
3457 scaled w
= char_width
(font
(p
), character
(p
));
3461 scaled glyph_height
(halfword p
)
3463 scaled w
= char_height
(font
(p
), character
(p
)) + y_displace
(p
);
3469 scaled glyph_depth
(halfword p
)
3471 scaled w
= char_depth
(font
(p
), character
(p
));
3472 if
(y_displace
(p
) > 0)
3473 w
= w
- y_displace
(p
);
3479 @ A |disc_node|
, which occurs only in horizontal lists
, specifies a
3480 ``dis\
-cretion\
-ary'' line break. If such a break occurs at node |p|
, the text
3481 that starts at |pre_break
(p
)| will precede the break
, the text that starts at
3482 |post_break
(p
)| will follow the break
, and text that appears in
3483 |no_break
(p
)| nodes will be ignored. For example
, an ordinary
3484 discretionary hyphen
, indicated by `\.
{\\
-}'
, yields a |disc_node| with
3485 |pre_break| pointing to a |char_node| containing a hyphen
, |post_break
=null|
,
3486 and |no_break
=null|.
3488 {TODO
: Knuth said
: All three of the discretionary texts must be lists
3489 that consist entirely of character
, kern
, box and rule nodes.
}
3491 If |subtype
(p
)=automatic_disc|
, the |ex_hyphen_penalty| will be charged for this
3492 break. Otherwise the |hyphen_penalty| will be charged. The texts will
3493 actually be substituted into the list by the line-breaking algorithm if it
3494 decides to make the break
, and the discretionary node will disappear at
3495 that time
; thus
, the output routine sees only discretionaries that were
3499 halfword new_disc
(void
)
3500 { /* creates an empty |disc_node|
*/
3501 halfword p
= new_node
(disc_node
, 0);
3502 disc_penalty
(p
) = int_par
(hyphen_penalty_code
);
3506 @ A |whatsit_node| is a wild card reserved for extensions to \TeX. The
3507 |subtype| field in its first word says what `\\
{whatsit
}' it is
, and
3508 implicitly determines the node size
(which must be
2 or more
) and the
3509 format of the remaining words. When a |whatsit_node| is encountered
3510 in a list
, special actions are invoked
; knowledgeable people who are
3511 careful not to mess up the rest of \TeX\ are able to make \TeX\ do new
3512 things by adding code at the end of the program. For example
, there
3513 might be a `\TeX nicolor' extension to specify different colors of ink
,
3514 @^extensions to \TeX@
>
3515 and the whatsit node might contain the desired parameters.
3517 The present implementation of \TeX\ treats the features associated with
3518 `\.
{\\write
}' and `\.
{\\special
}' as if they were extensions
, in order to
3519 illustrate how such routines might be coded. We shall defer further
3520 discussion of extensions until the end of this program.
3522 @ A |math_node|
, which occurs only in horizontal lists
, appears before and
3523 after mathematical formulas. The |subtype| field is |before| before the
3524 formula and |after| after it. There is a |surround| field
, which represents
3525 the amount of surrounding space inserted by \.
{\\mathsurround
}.
3528 halfword new_math
(scaled w
, int s
)
3530 halfword p
= new_node
(math_node
, s
);
3535 @ \TeX\ makes use of the fact that |hlist_node|
, |vlist_node|
,
3536 |rule_node|
, |ins_node|
, |mark_node|
, |adjust_node|
,
3537 |disc_node|
, |whatsit_node|
, and |math_node| are at the low end of the
3538 type codes
, by permitting a break at glue in a list if and only if the
3539 |type| of the previous node is less than |math_node|. Furthermore
, a
3540 node is discarded after a break if its type is |math_node| or~more.
3542 @ A |glue_node| represents glue in a list. However
, it is really only
3543 a pointer to a separate glue specification
, since \TeX\ makes use of the
3544 fact that many essentially identical nodes of glue are usually present.
3545 If |p| points to a |glue_node|
, |glue_ptr
(p
)| points to
3546 another packet of words that specify the stretch and shrink components
, etc.
3548 Glue nodes also serve to represent leaders
; the |subtype| is used to
3549 distinguish between ordinary glue
(which is called |normal|
) and the three
3550 kinds of leaders
(which are called |a_leaders|
, |c_leaders|
, and |x_leaders|
).
3551 The |leader_ptr| field points to a rule node or to a box node containing the
3552 leaders
; it is set to |null| in ordinary glue nodes.
3554 Many kinds of glue are computed from \TeX's ``skip'' parameters
, and
3555 it is helpful to know which parameter has led to a particular glue node.
3556 Therefore the |subtype| is set to indicate the source of glue
, whenever
3557 it originated as a parameter. We will be defining symbolic names for the
3558 parameter numbers later
(e.g.
, |line_skip_code
=0|
, |baseline_skip_code
=1|
,
3559 etc.
); it suffices for now to say that the |subtype| of parametric glue
3560 will be the same as the parameter number
, plus~one.
3562 @ In math formulas there are two more possibilities for the |subtype| in a
3563 glue node
: |mu_glue| denotes an \.
{\\mskip
} (where the units are scaled \.
{mu
}
3564 instead of scaled \.
{pt
}); and |cond_math_glue| denotes the `\.
{\\nonscript
}'
3565 feature that cancels the glue node immediately following if it appears
3568 @ A glue specification has a halfword reference count in its first word
,
3569 @^reference counts@
>
3570 representing |null| plus the number of glue nodes that point to it
(less one
).
3571 Note that the reference count appears in the same position as
3572 the |link| field in list nodes
; this is the field that is initialized
3573 to |null| when a node is allocated
, and it is also the field that is flagged
3574 by |empty_flag| in empty nodes.
3576 Glue specifications also contain three |scaled| fields
, for the |width|
,
3577 |stretch|
, and |shrink| dimensions. Finally
, there are two one-byte
3578 fields called |stretch_order| and |shrink_order|
; these contain the
3579 orders of infinity
(|normal|
, |sfi|
, |fil|
, |fill|
, or |filll|
)
3580 corresponding to the stretch and shrink values.
3582 @ Here is a function that returns a pointer to a copy of a glue spec.
3583 The reference count in the copy is |null|
, because there is assumed
3584 to be exactly one reference to the new specification.
3587 halfword new_spec
(halfword p
)
3588 { /* duplicates a glue specification
*/
3589 halfword q
= copy_node
(p
);
3590 glue_ref_count
(q
) = null
;
3594 @ And here's a function that creates a glue node for a given parameter
3595 identified by its code number
; for example
,
3596 |new_param_glue
(line_skip_code
)| returns a pointer to a glue node for the
3597 current \.
{\\lineskip
}.
3600 halfword new_param_glue
(int n
)
3602 halfword p
= new_node
(glue_node
, n
+ 1);
3603 halfword q
= glue_par
(n
);
3605 incr
(glue_ref_count
(q
));
3609 @ Glue nodes that are more or less anonymous are created by |new_glue|
,
3610 whose argument points to a glue specification.
3613 halfword new_glue
(halfword q
)
3615 halfword p
= new_node
(glue_node
, normal
);
3617 incr
(glue_ref_count
(q
));
3621 @ Still another subroutine is needed
: This one is sort of a combination
3622 of |new_param_glue| and |new_glue|. It creates a glue node for one of
3623 the current glue parameters
, but it makes a fresh copy of the glue
3624 specification
, since that specification will probably be subject to change
,
3625 while the parameter will stay put. The global variable |temp_ptr| is
3626 set to the address of the new spec.
3629 halfword new_skip_param
(int n
)
3631 halfword p
; /* the new node
*/
3632 temp_ptr
= new_spec
(glue_par
(n
));
3633 p
= new_glue
(temp_ptr
);
3634 glue_ref_count
(temp_ptr
) = null
;
3635 subtype
(p
) = (quarterword
) (n
+ 1);
3639 @ A |kern_node| has a |width| field to specify a
(normally negative
)
3640 amount of spacing. This spacing correction appears in horizontal lists
3641 between letters like A and V when the font designer said that it looks
3642 better to move them closer together or further apart. A kern node can
3643 also appear in a vertical list
, when its `|width|' denotes additional
3644 spacing in the vertical direction. The |subtype| is either |normal|
(for
3645 kerns inserted from font information or math mode calculations
) or |explicit|
3646 (for kerns inserted from \.
{\\kern
} and \.
{\\
/} commands
) or |acc_kern|
3647 (for kerns inserted from non-math accents
) or |mu_glue|
(for kerns
3648 inserted from \.
{\\mkern
} specifications in math formulas
).
3650 @ The |new_kern| function creates a kern node having a given width.
3653 halfword new_kern
(scaled w
)
3655 halfword p
= new_node
(kern_node
, normal
);
3660 @ A |penalty_node| specifies the penalty associated with line or page
3661 breaking
, in its |penalty| field. This field is a fullword integer
, but
3662 the full range of integer values is not used
: Any penalty |
>=10000| is
3663 treated as infinity
, and no break will be allowed for such high values.
3664 Similarly
, any penalty |
<=-10000| is treated as negative infinity
, and a
3665 break will be forced.
3667 @ Anyone who has been reading the last few sections of the program will
3668 be able to guess what comes next.
3671 halfword new_penalty
(int m
)
3673 halfword p
= new_node
(penalty_node
, 0); /* the |subtype| is not used
*/
3678 @ You might think that we have introduced enough node types by now. Well
,
3679 almost
, but there is one more
: An |unset_node| has nearly the same format
3680 as an |hlist_node| or |vlist_node|
; it is used for entries in \.
{\\halign
}
3681 or \.
{\\valign
} that are not yet in their final form
, since the box
3682 dimensions are their ``natural'' sizes before any glue adjustment has been
3683 made. The |glue_set| word is not present
; instead
, we have a |glue_stretch|
3684 field
, which contains the total stretch of order |glue_order| that is
3685 present in the hlist or vlist being boxed.
3686 Similarly
, the |shift_amount| field is replaced by a |glue_shrink| field
,
3687 containing the total shrink of order |glue_sign| that is present.
3688 The |subtype| field is called |span_count|
; an unset box typically
3689 contains the data for |qo
(span_count
)+1| columns.
3690 Unset nodes will be changed to box nodes when alignment is completed.
3692 In fact
, there are still more types coming. When we get to math formula
3693 processing we will see that a |style_node| has |type
=14|
; and a number
3694 of larger type codes will also be defined
, for use in math mode only.
3696 Warning
: If any changes are made to these data structure layouts
, such as
3697 changing any of the node sizes or even reordering the words of nodes
,
3698 the |copy_node_list| procedure and the memory initialization code
3699 below may have to be changed. Such potentially dangerous parts of the
3700 program are listed in the index under `data structure assumptions'.
3701 @
!@^data structure assumptions@
>
3702 However
, other references to the nodes are made symbolically in terms of
3703 the \.
{WEB
} macro definitions above
, so that format changes will leave
3704 \TeX's other algorithms intact.
3705 @^system dependencies@
>
3707 @ This function creates a |local_paragraph| node
3711 halfword make_local_par_node
(void
)
3714 halfword p
= new_node
(local_par_node
,0);
3715 local_pen_inter
(p
) = local_inter_line_penalty
;
3716 local_pen_broken
(p
) = local_broken_penalty
;
3717 if
(local_left_box
!= null
) {
3718 q
= copy_node_list
(local_left_box
);
3719 local_box_left
(p
) = q
;
3720 local_box_left_width
(p
) = width
(local_left_box
);
3722 if
(local_right_box
!= null
) {
3723 q
= copy_node_list
(local_right_box
);
3724 local_box_right
(p
) = q
;
3725 local_box_right_width
(p
) = width
(local_right_box
);
3727 local_par_dir
(p
) = par_direction
;