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", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL
94 const char
*node_fields_glue
[] = {
95 "attr", "leader", "width", "stretch", "shrink", "stretch_order", "shrink_order", 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",
116 "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL
118 const char
*node_fields_splitup
[] = {
119 "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins", NULL
121 const char
*node_fields_attribute
[] = {
122 "number", "value", NULL
124 const char
*node_fields_glue_spec
[] = {
125 "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL
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", 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", NULL
242 const char
*node_fields_whatsit_pdf_thread
[] = {
243 "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL
245 const char
*node_fields_whatsit_pdf_start_thread
[] = {
246 "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL
248 const char
*node_fields_whatsit_pdf_end_thread
[] = {
251 const char
*node_fields_whatsit_pdf_colorstack
[] = {
252 "attr", "stack", "cmd", "data", NULL
254 const char
*node_fields_whatsit_pdf_setmatrix
[] = {
257 const char
*node_fields_whatsit_pdf_save
[] = {
260 const char
*node_fields_whatsit_pdf_restore
[] = {
266 const char
*node_subtypes_glue
[] = {
267 "userskip", "lineskip", "baselineskip", "parskip", "abovedisplayskip", "belowdisplayskip",
268 "abovedisplayshortskip", "belowdisplayshortskip", "leftskip", "rightskip", "topskip",
269 "splittopskip", "tabskip", "spaceskip", "xspaceskip", "parfillskip",
270 "mathskip", "thinmuskip", "medmuskip", "thickmuskip", NULL
272 const char
*node_subtypes_mathglue
[] = { /* 98+ */
273 "conditionalmathskip", "muglue", NULL
275 const char
*node_subtypes_leader
[] = { /* 100+ */
276 "leaders", "cleaders", "xleaders", "gleaders", NULL
278 const char
*node_subtypes_fill
[] = {
279 "stretch", "fi", "fil", "fill", "filll", NULL
281 const char
*node_subtypes_boundary
[] = {
282 "cancel", "user", "protrusion", "word", NULL
284 const char
*node_subtypes_penalty
[] = {
287 const char
*node_subtypes_kern
[] = {
288 "fontkern", "userkern", "accentkern", "italiccorrection", NULL
290 const char
*node_subtypes_rule
[] = {
291 "normal", "box", "image", "empty", "user", NULL
293 const char
*node_subtypes_glyph
[] = {
294 "character", "glyph", "ligature", "ghost", "left", "right", NULL
296 const char
*node_subtypes_disc
[] = {
297 "discretionary", "explicit", "automatic", "regular", "first", "second", NULL
299 const char
*node_subtypes_marginkern
[] = {
300 "left", "right", NULL
302 const char
*node_subtypes_list
[] = {
303 "unknown", "line", "box", "indent", "alignment", "cell", "equation", "equationnumber", NULL
305 const char
*node_subtypes_adjust
[] = {
306 "normal", "pre", NULL
308 const char
*node_subtypes_math
[] = {
309 "beginmath", "endmath", NULL
311 const char
*node_subtypes_noad
[] = {
312 "ord", "opdisplaylimits", "oplimits", "opnolimits", "bin", "rel", "open", "close",
313 "punct", "inner", "under", "over", "vcenter", NULL
315 const char
*node_subtypes_radical
[] = {
316 "radical", "uradical", "uroot", "uunderdelimiter", "uoverdelimiter", "udelimiterunder",
317 "udelimiterover", NULL
319 const char
*node_subtypes_accent
[] = {
320 "bothflexible", "fixedtop", "fixedbottom", "fixedboth", NULL,
322 const char
*node_subtypes_fence
[] = {
323 "unset", "left", "middle", "right", NULL
326 node_info node_data
[] = { /* the last entry in a row is the etex number
*/
327 { hlist_node
, box_node_size
, node_fields_list
, "hlist", 1 },
328 { vlist_node
, box_node_size
, node_fields_list
, "vlist", 2 },
329 { rule_node
, rule_node_size
, node_fields_rule
, "rule", 3 },
330 { ins_node
, ins_node_size
, node_fields_insert
, "ins", 4 },
331 { mark_node
, mark_node_size
, node_fields_mark
, "mark", 5 },
332 { adjust_node
, adjust_node_size
, node_fields_adjust
, "adjust", 6 },
333 { boundary_node
, boundary_size
, node_fields_boundary
, "boundary", -1 },
334 { disc_node
, disc_node_size
, node_fields_disc
, "disc", 8 },
335 { whatsit_node
, -1, NULL, "whatsit", 9 },
336 { local_par_node
, local_par_size
, node_fields_local_par
, "local_par", -1 },
337 { dir_node
, dir_node_size
, node_fields_dir
, "dir", -1 },
338 { math_node
, math_node_size
, node_fields_math
, "math", 10 },
339 { glue_node
, glue_node_size
, node_fields_glue
, "glue", 11 },
340 { kern_node
, kern_node_size
, node_fields_kern
, "kern", 12 },
341 { penalty_node
, penalty_node_size
, node_fields_penalty
, "penalty", 13 },
342 { unset_node
, box_node_size
, node_fields_unset
, "unset", 14 },
343 { style_node
, style_node_size
, node_fields_style
, "style", 15 },
344 { choice_node
, style_node_size
, node_fields_choice
, "choice", 15 },
345 { simple_noad
, noad_size
, node_fields_ord
, "noad", 15 },
346 { radical_noad
, radical_noad_size
, node_fields_radical
, "radical", 15 },
347 { fraction_noad
, fraction_noad_size
, node_fields_fraction
, "fraction", 15 },
348 { accent_noad
, accent_noad_size
, node_fields_accent
, "accent", 15 },
349 { fence_noad
, fence_noad_size
, node_fields_fence
, "fence", 15 },
350 { math_char_node
, math_kernel_node_size
, node_fields_math_char
, "math_char", 15 },
351 { sub_box_node
, math_kernel_node_size
, node_fields_sub_box
, "sub_box", 15 },
352 { sub_mlist_node
, math_kernel_node_size
, node_fields_sub_mlist
, "sub_mlist", 15 },
353 { math_text_char_node
, math_kernel_node_size
, node_fields_math_text_char
, "math_text_char", 15 },
354 { delim_node
, math_shield_node_size
, node_fields_delim
, "delim", 15 },
355 { margin_kern_node
, margin_kern_node_size
, node_fields_margin_kern
, "margin_kern", -1 },
356 { glyph_node
, glyph_node_size
, node_fields_glyph
, "glyph", 0 },
357 { align_record_node
, box_node_size
, NULL, "align_record", -1 },
358 { pseudo_file_node
, pseudo_file_node_size
, NULL, "pseudo_file", -1 },
359 { pseudo_line_node
, variable_node_size
, NULL, "pseudo_line", -1 },
360 { inserting_node
, page_ins_node_size
, node_fields_inserting
, "page_insert", -1 },
361 { split_up_node
, page_ins_node_size
, node_fields_splitup
, "split_insert", -1 },
362 { expr_node
, expr_node_size
, NULL, "expr_stack", -1 },
363 { nesting_node
, nesting_node_size
, NULL, "nested_list", -1 },
364 { span_node
, span_node_size
, NULL, "span", -1 },
365 { attribute_node
, attribute_node_size
, node_fields_attribute
, "attribute", -1 },
366 { glue_spec_node
, glue_spec_size
, node_fields_glue_spec
, "glue_spec", -1 },
367 { attribute_list_node
, attribute_node_size
, node_fields_attribute_list
, "attribute_list", -1 },
368 { temp_node
, temp_node_size
, NULL, "temp", -1 },
369 { align_stack_node
, align_stack_node_size
, NULL, "align_stack", -1 },
370 { movement_node
, movement_node_size
, NULL, "movement_stack", -1 },
371 { if_node
, if_node_size
, NULL, "if_stack", -1 },
372 { unhyphenated_node
, active_node_size
, NULL, "unhyphenated", -1 },
373 { hyphenated_node
, active_node_size
, NULL, "hyphenated", -1 },
374 { delta_node
, delta_node_size
, NULL, "delta", -1 },
375 { passive_node
, passive_node_size
, NULL, "passive", -1 },
376 { shape_node
, variable_node_size
, NULL, "shape", -1 },
377 { -1, -1, NULL, NULL, -1 },
380 #define last_normal_node shape_node
382 const char
*node_subtypes_pdf_destination
[] = {
383 "xyz", "fit", "fith", "fitv", "fitb", "fitbh", "fitbv", "fitr", NULL
385 const char
*node_subtypes_pdf_literal
[] = {
386 "origin", "page", "direct", NULL
389 node_info whatsit_node_data
[] = {
390 { open_node
, open_node_size
, node_fields_whatsit_open
, "open", -1 },
391 { write_node
, write_node_size
, node_fields_whatsit_write
, "write", -1 },
392 { close_node
, close_node_size
, node_fields_whatsit_close
, "close", -1 },
393 { special_node
, special_node_size
, node_fields_whatsit_special
, "special", -1 },
394 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
395 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
396 { save_pos_node
, save_pos_node_size
, node_fields_whatsit_save_pos
, "save_pos", -1 },
397 { late_lua_node
, late_lua_node_size
, node_fields_whatsit_late_lua
, "late_lua", -1 },
398 { user_defined_node
, user_defined_node_size
, node_fields_whatsit_user_defined
, "user_defined", -1 },
399 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
400 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
401 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
402 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
403 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
404 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
405 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
406 /* here starts the dvi backend section
, todo
: a separate list
*/
407 /* nothing for dvi
*/
408 /* here starts the pdf backend section
, todo
: a separate list
*/
409 { pdf_literal_node
, write_node_size
, node_fields_whatsit_pdf_literal
, "pdf_literal", -1 },
410 { pdf_refobj_node
, pdf_refobj_node_size
, node_fields_whatsit_pdf_refobj
, "pdf_refobj", -1 },
411 { pdf_annot_node
, pdf_annot_node_size
, node_fields_whatsit_pdf_annot
, "pdf_annot", -1 },
412 { pdf_start_link_node
, pdf_annot_node_size
, node_fields_whatsit_pdf_start_link
, "pdf_start_link", -1 },
413 { pdf_end_link_node
, pdf_end_link_node_size
, node_fields_whatsit_pdf_end_link
, "pdf_end_link", -1 },
414 { pdf_dest_node
, pdf_dest_node_size
, node_fields_whatsit_pdf_dest
, "pdf_dest", -1 },
415 { pdf_action_node
, pdf_action_size
, node_fields_whatsit_pdf_action
, "pdf_action", -1 },
416 { pdf_thread_node
, pdf_thread_node_size
, node_fields_whatsit_pdf_thread
, "pdf_thread", -1 },
417 { pdf_start_thread_node
, pdf_thread_node_size
, node_fields_whatsit_pdf_start_thread
, "pdf_start_thread", -1 },
418 { pdf_end_thread_node
, pdf_end_thread_node_size
, node_fields_whatsit_pdf_end_thread
, "pdf_end_thread", -1 },
419 { pdf_thread_data_node
, pdf_thread_node_size
, NULL, "pdf_thread_data", -1 },
420 { pdf_link_data_node
, pdf_annot_node_size
, NULL, "pdf_link_data", -1 },
421 { pdf_colorstack_node
, pdf_colorstack_node_size
, node_fields_whatsit_pdf_colorstack
, "pdf_colorstack", -1 },
422 { pdf_setmatrix_node
, pdf_setmatrix_node_size
, node_fields_whatsit_pdf_setmatrix
, "pdf_setmatrix", -1 },
423 { pdf_save_node
, pdf_save_node_size
, node_fields_whatsit_pdf_save
, "pdf_save", -1 },
424 { pdf_restore_node
, pdf_restore_node_size
, node_fields_whatsit_pdf_restore
, "pdf_restore", -1 },
426 { -1, -1, NULL, NULL, -1 },
429 #define last_whatsit_node pdf_restore_node
432 When we copy a node list
, there are several possibilities
: we do the same as a new node
,
433 we copy the entry to the table in properties
(a reference
), we do a deep copy of a table
434 in the properties
, we create a new table and give it the original one as a metatable.
435 After some experiments
(that also included timing
) with these scenarios I decided that a
436 deep copy made no sense
, nor did nilling. In the end both the shallow copy and the metatable
437 variant were both ok
, although the second ons is slower. The most important aspect to keep
438 in mind is that references to other nodes in properties no longer can be valid for that
439 copy. We could use two tables
(one unique and one shared
) or metatables but that only
442 When defining a new node
, we could already allocate a table but it is rather easy to do
443 that at the lua end e.g. using a metatable __index method. That way it is under macro
446 When deleting a node
, we could keep the slot
(e.g. setting it to false
) but it could make
447 memory consumption raise unneeded when we have temporary large node lists and after that
450 So
, in the end this is what we ended up with. For the record
, I also experimented with the
453 - copy attributes to the properties so that we have fast access at the lua end
: in the end
454 the overhead is not compensated by speed and convenience
, in fact
, attributes are not
455 that slow when it comes to accessing them
457 - a bitset in the node but again the gain compared to attributes is neglectable and it also
458 demands a pretty string agreement over what bit represents what
, and this is unlikely to
459 succeed in the tex community
(I could use it for font handling
, which is cross package
,
460 but decided that it doesn't pay off
462 In case one wonders why properties make sense then
, well
, it is not so much speed that we
463 gain
, but more convenience
: storing all kind of
(temporary
) data in attributes is no fun and
464 this mechanism makes sure that properties are cleaned up when a node is freed. Also
, the
465 advantage of a more or less global properties table is that we stay at the lua end. An
466 alternative is to store a reference in the node itself but that is complicated by the fact
467 that the register has some limitations
(no numeric keys
) and we also don't want to mess with
471 int lua_properties_level
= 0 ; /* can be private
*/
472 int lua_properties_enabled
= 0 ;
473 int lua_properties_use_metatable
= 0 ;
476 We keep track of nesting so that we don't oveflow the stack
, and
, what is more
477 important
, don't keep resolving the registry index.
480 #define lua_properties_push do
{ \
481 if
(lua_properties_enabled
) { \
482 lua_properties_level
= lua_properties_level
+ 1 ; \
483 if
(lua_properties_level
== 1) { \
484 lua_get_metatablelua_l
(Luas
,node_properties
); \
489 #define lua_properties_pop do
{ \
490 if
(lua_properties_enabled
) { \
491 if
(lua_properties_level
== 1) \
493 lua_properties_level
= lua_properties_level
- 1 ; \
497 /* No setting is needed
: */
499 #define lua_properties_set
(target
) do
{ \
502 /* Resetting boils down to nilling.
*/
504 #define lua_properties_reset
(target
) do
{ \
505 if
(lua_properties_enabled
) { \
506 if
(lua_properties_level
== 0) { \
507 lua_get_metatablelua_l
(Luas
,node_properties
); \
509 lua_rawseti
(Luas
,-2,target
); \
513 lua_rawseti
(Luas
,-2,target
); \
519 For a moment I considered supporting all kind of data types but in practice
520 that makes no sense. So we stick to a cheap shallow copy with as option a
521 metatable. Btw
, a deep copy would look like this
:
523 static void copy_lua_table
(lua_State
* L
, int index
) {
526 while
(lua_next
(L
, index-1
) != 0) {
527 lua_pushvalue
(L
, -2);
529 if
(lua_type
(L
,-1)==LUA_TTABLE
)
530 copy_lua_table
(L
,-1);
536 #define lua_properties_copy
(target
, source
) do
{ \
537 if
(lua_properties_enabled
) { \
538 lua_pushinteger
(Luas
,source
); \
539 lua_rawget
(Luas
,-2); \
540 if
(lua_type
(Luas
,-1)==LUA_TTABLE
) { \
541 copy_lua_table
(Luas
,-1); \
542 lua_pushinteger
(Luas
,target
); \
543 lua_insert
(Luas
,-2); \
544 lua_rawset
(Luas
,-3); \
553 /* isn't there a faster way to metatable?
*/
555 #define lua_properties_copy
(target
,source
) do
{ \
556 if
(lua_properties_enabled
) { \
557 if
(lua_properties_level
== 0) { \
558 lua_get_metatablelua_l
(Luas
,node_properties
); \
559 lua_rawgeti
(Luas
,-1,source
); \
560 if
(lua_type
(Luas
,-1)==LUA_TTABLE
) { \
561 if
(lua_properties_use_metatable
) { \
562 lua_newtable
(Luas
); \
563 lua_insert
(Luas
,-2); \
564 lua_setfield
(Luas
,-2,"__index"); \
565 lua_newtable
(Luas
); \
566 lua_insert
(Luas
,-2); \
567 lua_setmetatable
(Luas
,-2); \
569 lua_rawseti
(Luas
,-2,target
); \
575 lua_rawgeti
(Luas
,-1,source
); \
576 if
(lua_type
(Luas
,-1)==LUA_TTABLE
) { \
577 if
(lua_properties_use_metatable
) { \
578 lua_newtable
(Luas
); \
579 lua_insert
(Luas
,-2); \
580 lua_setfield
(Luas
,-2,"__index"); \
581 lua_newtable
(Luas
); \
582 lua_insert
(Luas
,-2); \
583 lua_setmetatable
(Luas
,-2); \
585 lua_rawseti
(Luas
,-2,target
); \
593 /* Here end the property handlers.
*/
596 int valid_node
(halfword p
)
598 if
(p
> my_prealloc
&& p < var_mem_max) {
599 #ifdef CHECK_NODE_USAGE
600 if
(varmem_sizes
[p
] > 0) {
611 static int test_count
= 1;
613 #define dorangetest
(a
,b
,c
) do
{ \
614 if
(!(b
>=0 && b<c)) { \
615 fprintf
(stdout
,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \
616 (int
)a
, (int
)b
, (int
)c
, __LINE__
,test_count
); \
617 confusion
("node range test failed"); \
620 #define dotest
(a
,b
,c
) do
{ \
622 fprintf
(stdout
,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \
623 (int
)a
, (int
)b
, (int
)c
, __LINE__
,test_count
); \
624 confusion
("node test failed"); \
627 #define check_action_ref
(a
) { dorangetest
(p
,a
,var_mem_max
); }
628 #define check_attribute_ref
(a
) { dorangetest
(p
,a
,var_mem_max
); }
629 #define check_token_ref
(a
) { confusion
("fuzzy token cleanup in node"); }
631 #ifdef CHECK_NODE_USAGE
633 static void check_static_node_mem
(void
)
635 dotest
(zero_glue
, width
(zero_glue
), 0);
636 dotest
(zero_glue
, type
(zero_glue
), glue_spec_node
);
637 dotest
(zero_glue
, vlink
(zero_glue
), null
);
638 dotest
(zero_glue
, stretch
(zero_glue
), 0);
639 dotest
(zero_glue
, stretch_order
(zero_glue
), normal
);
640 dotest
(zero_glue
, shrink
(zero_glue
), 0);
641 dotest
(zero_glue
, shrink_order
(zero_glue
), normal
);
643 dotest
(sfi_glue
, width
(sfi_glue
), 0);
644 dotest
(sfi_glue
, type
(sfi_glue
), glue_spec_node
);
645 dotest
(sfi_glue
, vlink
(sfi_glue
), null
);
646 dotest
(sfi_glue
, stretch
(sfi_glue
), 0);
647 dotest
(sfi_glue
, stretch_order
(sfi_glue
), sfi
);
648 dotest
(sfi_glue
, shrink
(sfi_glue
), 0);
649 dotest
(sfi_glue
, shrink_order
(sfi_glue
), normal
);
651 dotest
(fil_glue
, width
(fil_glue
), 0);
652 dotest
(fil_glue
, type
(fil_glue
), glue_spec_node
);
653 dotest
(fil_glue
, vlink
(fil_glue
), null
);
654 dotest
(fil_glue
, stretch
(fil_glue
), unity
);
655 dotest
(fil_glue
, stretch_order
(fil_glue
), fil
);
656 dotest
(fil_glue
, shrink
(fil_glue
), 0);
657 dotest
(fil_glue
, shrink_order
(fil_glue
), normal
);
659 dotest
(fill_glue
, width
(fill_glue
), 0);
660 dotest
(fill_glue
, type
(fill_glue
), glue_spec_node
);
661 dotest
(fill_glue
, vlink
(fill_glue
), null
);
662 dotest
(fill_glue
, stretch
(fill_glue
), unity
);
663 dotest
(fill_glue
, stretch_order
(fill_glue
), fill
);
664 dotest
(fill_glue
, shrink
(fill_glue
), 0);
665 dotest
(fill_glue
, shrink_order
(fill_glue
), normal
);
667 dotest
(ss_glue
, width
(ss_glue
), 0);
668 dotest
(ss_glue
, type
(ss_glue
), glue_spec_node
);
669 dotest
(ss_glue
, vlink
(ss_glue
), null
);
670 dotest
(ss_glue
, stretch
(ss_glue
), unity
);
671 dotest
(ss_glue
, stretch_order
(ss_glue
), fil
);
672 dotest
(ss_glue
, shrink
(ss_glue
), unity
);
673 dotest
(ss_glue
, shrink_order
(ss_glue
), fil
);
675 dotest
(fil_neg_glue
, width
(fil_neg_glue
), 0);
676 dotest
(fil_neg_glue
, type
(fil_neg_glue
), glue_spec_node
);
677 dotest
(fil_neg_glue
, vlink
(fil_neg_glue
), null
);
678 dotest
(fil_neg_glue
, stretch
(fil_neg_glue
), -unity
);
679 dotest
(fil_neg_glue
, stretch_order
(fil_neg_glue
), fil
);
680 dotest
(fil_neg_glue
, shrink
(fil_neg_glue
), 0);
681 dotest
(fil_neg_glue
, shrink_order
(fil_neg_glue
), normal
);
684 static void node_mem_dump
(halfword p
)
687 for
(r
= my_prealloc
+ 1; r
< var_mem_max
; r
++) {
690 while
(s
> my_prealloc
&& varmem_sizes[s] == 0) {
696 && (r - s) < get_node_size(type(s), subtype(s))
698 if
(type
(s
) == disc_node
) {
699 fprintf
(stdout
," pointed to from %s node %d (vlink %d, alink %d): ",
700 get_node_name
(type
(s
), subtype
(s
)), (int
) s
,
701 (int
) vlink
(s
), (int
) alink
(s
));
702 fprintf
(stdout
, "pre_break(%d,%d,%d), ",
703 (int
) vlink_pre_break
(s
), (int
) tlink
(pre_break
(s
)),
704 (int
) alink
(pre_break
(s
)));
705 fprintf
(stdout
, "post_break(%d,%d,%d), ",
706 (int
) vlink_post_break
(s
),
707 (int
) tlink
(post_break
(s
)),
708 (int
) alink
(post_break
(s
)));
709 fprintf
(stdout
, "no_break(%d,%d,%d)",
710 (int
) vlink_no_break
(s
), (int
) tlink
(no_break
(s
)),
711 (int
) alink
(no_break
(s
)));
712 fprintf
(stdout
, "\n");
715 ||
(type
(s
) == glyph_node
&& lig_ptr (s) == p)
716 ||
(type
(s
) == vlist_node
&& list_ptr(s) == p)
717 ||
(type
(s
) == hlist_node
&& list_ptr(s) == p)
718 ||
(type
(s
) == unset_node
&& list_ptr(s) == p)
719 ||
(type
(s
) == ins_node
&& ins_ptr (s) == p)
721 fprintf
(stdout
," pointed to from %s node %d (vlink %d, alink %d): ",
722 get_node_name
(type
(s
), subtype
(s
)), (int
) s
,
723 (int
) vlink
(s
), (int
) alink
(s
));
724 if
(type
(s
) == glyph_node
) {
725 fprintf
(stdout
, "lig_ptr(%d)", (int
) lig_ptr
(s
));
726 } else if
(type
(s
) == vlist_node || type
(s
) == hlist_node
) {
727 fprintf
(stdout
, "list_ptr(%d)", (int
) list_ptr
(s
));
729 fprintf
(stdout
, "\n");
731 if
((type
(s
) != penalty_node
) && (type(s) != math_node) && (type(s) != kern_node)) {
732 fprintf
(stdout
, " pointed to from %s node %d\n",
733 get_node_name
(type
(s
), subtype
(s
)), (int
) s
);
744 static int free_error
(halfword p
)
746 if
(p
> my_prealloc
&& p < var_mem_max) {
747 #ifdef CHECK_NODE_USAGE
749 if
(varmem_sizes
[p
] == 0) {
750 check_static_node_mem
();
751 for
(i
= (my_prealloc
+ 1); i
< var_mem_max
; i
++) {
752 if
(varmem_sizes
[i
] > 0) {
757 if
(type
(p
) == glyph_node
) {
758 formatted_error
("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int
) character
(p
), (int
) p
);
760 formatted_error
("nodes", "attempt to double-free %s node %d, ignored", get_node_name
(type
(p
), subtype
(p
)), (int
) p
);
767 formatted_error
("nodes", "attempt to free an impossible node %d", (int
) p
);
774 static int copy_error
(halfword p
)
776 if
(p
>= 0 && p < var_mem_max) {
777 #ifdef CHECK_NODE_USAGE
778 if
(p
> my_prealloc
&& varmem_sizes[p] == 0) {
779 if
(type
(p
) == glyph_node
) {
780 formatted_warning
("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int
) character
(p
), (int
) p
);
782 formatted_warning
("nodes", "attempt to copy free %s node %d, ignored", get_node_name
(type
(p
), subtype
(p
)), (int
) p
);
788 formatted_error
("nodes", "attempt to copy an impossible node %d", (int
) p
);
795 halfword new_node
(int i
, int j
)
797 int s
= get_node_size
(i
, j
);
798 halfword n
= get_node
(s
);
800 It should be possible to do this memset at |free_node
()|.
802 Both type
() and subtype
() will be set below
, and vlink
() is
803 set to null by |get_node
()|
, so we can do we clearing one
806 (void
) memset
((void
*) (varmem
+ n
+ 1), 0, (sizeof
(memory_word
) * ((unsigned
) s
- 1)));
816 pre_break
(n
) = pre_break_head
(n
);
817 type
(pre_break
(n
)) = nesting_node
;
818 subtype
(pre_break
(n
)) = pre_break_head
(0);
819 post_break
(n
) = post_break_head
(n
);
820 type
(post_break
(n
)) = nesting_node
;
821 subtype
(post_break
(n
)) = post_break_head
(0);
822 no_break
(n
) = no_break_head
(n
);
823 type
(no_break
(n
)) = nesting_node
;
824 subtype
(no_break
(n
)) = no_break_head
(0);
827 depth
(n
) = null_flag
;
828 height
(n
) = null_flag
;
829 width
(n
) = null_flag
;
832 rule_transform
(n
) = 0;
835 if
(j
== open_node
) {
836 open_name
(n
) = get_nullstr
();
837 open_area
(n
) = open_name
(n
);
838 open_ext
(n
) = open_name
(n
);
842 width
(n
) = null_flag
;
844 case pseudo_line_node
:
846 /* this is a trick that makes |pseudo_files| slightly slower
,
847 but the overall allocation faster then an explicit test
848 at the top of |new_node
()|.
851 free_node
(n
, variable_node_size
);
852 n
= slow_get_node
(j
);
853 (void
) memset
((void
*) (varmem
+ n
+ 1), 0, (sizeof
(memory_word
) * ((unsigned
) j
- 1)));
859 if
(int_par
(synctex_code
)) {
860 /* handle synctex extension
*/
863 synctex_tag_math
(n
) = cur_input.synctex_tag_field
;
864 synctex_line_math
(n
) = line
;
867 synctex_tag_glue
(n
) = cur_input.synctex_tag_field
;
868 synctex_line_glue
(n
) = line
;
872 synctex_tag_kern
(n
) = cur_input.synctex_tag_field
;
873 synctex_line_kern
(n
) = line
;
879 synctex_tag_box
(n
) = cur_input.synctex_tag_field
;
880 synctex_line_box
(n
) = line
;
883 synctex_tag_rule
(n
) = cur_input.synctex_tag_field
;
884 synctex_line_rule
(n
) = line
;
888 /* take care of attributes
*/
889 if
(nodetype_has_attributes
(i
)) {
890 build_attribute_list
(n
);
891 /* lua_properties_set
*/
893 type
(n
) = (quarterword
) i
;
894 subtype
(n
) = (quarterword
) j
;
898 halfword raw_glyph_node
(void
)
900 register halfword n
= get_node
(glyph_node_size
);
901 (void
) memset
((void
*) (varmem
+ n
+ 1), 0, (sizeof
(memory_word
) * (glyph_node_size
- 1)));
902 type
(n
) = glyph_node
;
907 halfword new_glyph_node
(void
)
909 register halfword n
= get_node
(glyph_node_size
);
910 (void
) memset
((void
*) (varmem
+ n
+ 1), 0, (sizeof
(memory_word
) * (glyph_node_size
- 1)));
911 type
(n
) = glyph_node
;
913 build_attribute_list
(n
);
914 /* lua_properties_set
*/
918 @ makes a duplicate of the node list that starts at |p| and returns a
919 pointer to the new list
922 halfword do_copy_node_list
(halfword p
, halfword end
)
924 halfword q
= null
; /* previous position in new list
*/
925 halfword h
= null
; /* head of the list
*/
926 register halfword s
;
927 lua_properties_push
; /* saves stack and time
*/
938 lua_properties_pop
; /* saves stack and time
*/
942 halfword copy_node_list
(halfword p
)
944 return do_copy_node_list
(p
, null
);
947 #define copy_sub_list
(target
,source
) do
{ \
948 if
(source
!= null
) { \
949 s
= do_copy_node_list
(source
, null
); \
956 #define copy_sub_node
(target
,source
) do
{ \
957 if
(source
!= null
) { \
958 s
= copy_node
(source
); \
965 @ make a dupe of a single node
968 static void copy_node_wrapup_core
(halfword p
, halfword r
)
971 switch
(subtype
(p
)) {
974 add_token_ref
(write_tokens
(p
));
979 case user_defined_node
:
980 switch
(user_node_type
(p
)) {
982 add_node_attr_ref
(user_node_value
(p
));
988 s
= copy_node_list
(user_node_value
(p
));
989 user_node_value
(r
) = s
;
992 /* |add_string_ref
(user_node_value
(p
));|
*/
995 add_token_ref
(user_node_value
(p
));
1004 void copy_node_wrapup_dvi
(halfword p
, halfword r
)
1008 void copy_node_wrapup_pdf
(halfword p
, halfword r
)
1010 switch
(subtype
(p
)) {
1011 case pdf_literal_node
:
1012 copy_pdf_literal
(r
, p
);
1014 case pdf_colorstack_node
:
1015 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
1016 add_token_ref
(pdf_colorstack_data
(p
));
1018 case pdf_setmatrix_node
:
1019 add_token_ref
(pdf_setmatrix_data
(p
));
1021 case pdf_annot_node
:
1022 add_token_ref
(pdf_annot_data
(p
));
1024 case pdf_start_link_node
:
1025 if
(pdf_link_attr
(r
) != null
)
1026 add_token_ref
(pdf_link_attr
(r
));
1027 add_action_ref
(pdf_link_action
(r
));
1030 if
(pdf_dest_named_id
(p
) > 0)
1031 add_token_ref
(pdf_dest_id
(p
));
1033 case pdf_thread_node
:
1034 case pdf_start_thread_node
:
1035 if
(pdf_thread_named_id
(p
) > 0)
1036 add_token_ref
(pdf_thread_id
(p
));
1037 if
(pdf_thread_attr
(p
) != null
)
1038 add_token_ref
(pdf_thread_attr
(p
));
1045 halfword copy_node
(const halfword p
)
1047 halfword r
; /* current node being fabricated for new list
*/
1048 halfword w
; /* whatsit subtype
*/
1049 register halfword s
; /* a helper variable for copying into variable mem
*/
1051 if
(copy_error
(p
)) {
1052 r
= new_node
(temp_node
, 0);
1055 i
= get_node_size
(type
(p
), subtype
(p
));
1058 (void
) memcpy
((void
*) (varmem
+ r
), (void
*) (varmem
+ p
), (sizeof
(memory_word
) * (unsigned
) i
));
1060 if
(int_par
(synctex_code
)) {
1061 /* handle synctex extension
*/
1064 synctex_tag_math
(r
) = cur_input.synctex_tag_field
;
1065 synctex_line_math
(r
) = line
;
1068 synctex_tag_kern
(r
) = cur_input.synctex_tag_field
;
1069 synctex_line_kern
(r
) = line
;
1073 if
(nodetype_has_attributes
(type
(p
))) {
1074 add_node_attr_ref
(node_attr
(p
));
1076 lua_properties_copy
(r
,p
);
1082 copy_sub_list
(lig_ptr
(r
),lig_ptr
(p
)) ;
1085 copy_sub_list
(leader_ptr
(r
),leader_ptr
(p
)) ;
1090 copy_sub_list
(list_ptr
(r
),list_ptr
(p
)) ;
1093 pre_break
(r
) = pre_break_head
(r
);
1094 if
(vlink_pre_break
(p
) != null
) {
1095 s
= copy_node_list
(vlink_pre_break
(p
));
1096 alink
(s
) = pre_break
(r
);
1097 tlink_pre_break
(r
) = tail_of_list
(s
);
1098 vlink_pre_break
(r
) = s
;
1100 assert
(tlink
(pre_break
(r
)) == null
);
1102 post_break
(r
) = post_break_head
(r
);
1103 if
(vlink_post_break
(p
) != null
) {
1104 s
= copy_node_list
(vlink_post_break
(p
));
1105 alink
(s
) = post_break
(r
);
1106 tlink_post_break
(r
) = tail_of_list
(s
);
1107 vlink_post_break
(r
) = s
;
1109 assert
(tlink_post_break
(r
) == null
);
1111 no_break
(r
) = no_break_head
(r
);
1112 if
(vlink
(no_break
(p
)) != null
) {
1113 s
= copy_node_list
(vlink_no_break
(p
));
1114 alink
(s
) = no_break
(r
);
1115 tlink_no_break
(r
) = tail_of_list
(s
);
1116 vlink_no_break
(r
) = s
;
1118 assert
(tlink_no_break
(r
) == null
);
1124 copy_sub_list
(ins_ptr
(r
),ins_ptr
(p
)) ;
1126 case margin_kern_node
:
1127 copy_sub_node
(margin_char
(r
),margin_char
(p
));
1130 add_token_ref
(mark_ptr
(p
));
1133 copy_sub_list
(adjust_ptr
(r
),adjust_ptr
(p
));
1136 copy_sub_list
(display_mlist
(r
),display_mlist
(p
)) ;
1137 copy_sub_list
(text_mlist
(r
),text_mlist
(p
)) ;
1138 copy_sub_list
(script_mlist
(r
),script_mlist
(p
)) ;
1139 copy_sub_list
(script_script_mlist
(r
),script_script_mlist
(p
)) ;
1142 copy_sub_list
(nucleus
(r
),nucleus
(p
)) ;
1143 copy_sub_list
(subscr
(r
),subscr
(p
)) ;
1144 copy_sub_list
(supscr
(r
),supscr
(p
)) ;
1147 copy_sub_list
(nucleus
(r
),nucleus
(p
)) ;
1148 copy_sub_list
(subscr
(r
),subscr
(p
)) ;
1149 copy_sub_list
(supscr
(r
),supscr
(p
)) ;
1150 copy_sub_node
(left_delimiter
(r
),left_delimiter
(p
)) ;
1151 copy_sub_list
(degree
(r
),degree
(p
)) ;
1154 copy_sub_list
(nucleus
(r
),nucleus
(p
)) ;
1155 copy_sub_list
(subscr
(r
),subscr
(p
)) ;
1156 copy_sub_list
(supscr
(r
),supscr
(p
)) ;
1157 copy_sub_list
(top_accent_chr
(r
),top_accent_chr
(p
)) ;
1158 copy_sub_list
(bot_accent_chr
(r
),bot_accent_chr
(p
)) ;
1159 copy_sub_list
(overlay_accent_chr
(r
),overlay_accent_chr
(p
)) ;
1162 copy_sub_node
(delimiter
(r
),delimiter
(p
)) ;
1165 case sub_mlist_node
:
1166 copy_sub_list
(math_list
(r
),math_list
(p
)) ;
1169 copy_sub_list
(numerator
(r
),numerator
(p
)) ;
1170 copy_sub_list
(denominator
(r
),denominator
(p
)) ;
1171 copy_sub_node
(left_delimiter
(r
),left_delimiter
(p
)) ;
1172 copy_sub_node
(right_delimiter
(r
),right_delimiter
(p
)) ;
1174 case glue_spec_node
:
1176 case local_par_node
:
1181 if
(w
>= backend_first_pdf_whatsit
) {
1182 copy_node_wrapup_pdf
(p
,r
);
1183 } else if
(w
>= backend_first_dvi_whatsit
) {
1184 copy_node_wrapup_dvi
(p
,r
);
1186 copy_node_wrapup_core
(p
,r
);
1195 #define free_sub_list
(source
) if
(source
!= null
) flush_node_list
(source
);
1196 #define free_sub_node
(source
) if
(source
!= null
) flush_node
(source
);
1200 static void flush_node_wrapup_core
(halfword p
)
1202 switch
(subtype
(p
)) {
1209 delete_token_ref
(write_tokens
(p
));
1214 case user_defined_node
:
1215 switch
(user_node_type
(p
)) {
1217 delete_attribute_ref
(user_node_value
(p
));
1222 flush_node_list
(user_node_value
(p
));
1225 /* |delete_string_ref
(user_node_value
(p
));|
*//* if this was mpost ..
*/
1228 delete_token_ref
(user_node_value
(p
));
1232 const char
*hlp
[] = {
1233 "The type of the value in a user defined whatsit node should be one",
1234 "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),",
1235 "or 't' (tokenlist). Yours has an unknown type, and therefore I don't",
1236 "know how to free the node's value. A memory leak may result.",
1239 tex_error
("Unidentified user defined whatsit", hlp
);
1247 void flush_node_wrapup_dvi
(halfword p
)
1251 void flush_node_wrapup_pdf
(halfword p
)
1253 switch
(subtype
(p
)) {
1255 case pdf_restore_node
:
1256 case pdf_refobj_node
:
1257 case pdf_end_link_node
:
1258 case pdf_end_thread_node
:
1260 case pdf_literal_node
:
1261 free_pdf_literal
(p
);
1263 case pdf_colorstack_node
:
1264 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
1265 delete_token_ref
(pdf_colorstack_data
(p
));
1267 case pdf_setmatrix_node
:
1268 delete_token_ref
(pdf_setmatrix_data
(p
));
1270 case pdf_annot_node
:
1271 delete_token_ref
(pdf_annot_data
(p
));
1273 case pdf_link_data_node
:
1275 case pdf_start_link_node
:
1276 if
(pdf_link_attr
(p
) != null
)
1277 delete_token_ref
(pdf_link_attr
(p
));
1278 delete_action_ref
(pdf_link_action
(p
));
1281 if
(pdf_dest_named_id
(p
) > 0)
1282 delete_token_ref
(pdf_dest_id
(p
));
1284 case pdf_action_node
:
1285 if
(pdf_action_type
(p
) == pdf_action_user
) {
1286 delete_token_ref
(pdf_action_tokens
(p
));
1288 if
(pdf_action_file
(p
) != null
)
1289 delete_token_ref
(pdf_action_file
(p
));
1290 if
(pdf_action_type
(p
) == pdf_action_page
)
1291 delete_token_ref
(pdf_action_tokens
(p
));
1292 else if
(pdf_action_named_id
(p
) > 0)
1293 delete_token_ref
(pdf_action_id
(p
));
1296 case pdf_thread_data_node
:
1298 case pdf_thread_node
:
1299 case pdf_start_thread_node
:
1300 if
(pdf_thread_named_id
(p
) > 0)
1301 delete_token_ref
(pdf_thread_id
(p
));
1302 if
(pdf_thread_attr
(p
) != null
)
1303 delete_token_ref
(pdf_thread_attr
(p
));
1308 void flush_node
(halfword p
)
1311 if
(p
== null
) /* legal
, but no-op
*/
1317 free_sub_list
(lig_ptr
(p
));
1320 free_sub_list
(leader_ptr
(p
));
1325 free_sub_list
(list_ptr
(p
));
1328 /* watch the start at temp node hack
*/
1329 free_sub_list
(vlink
(pre_break
(p
)));
1330 free_sub_list
(vlink
(post_break
(p
)));
1331 free_sub_list
(vlink
(no_break
(p
)));
1338 case glue_spec_node
:
1339 /* this allows free-ing of lua-allocated glue specs
*/
1340 //if
(valid_node
(p
)) {
1341 // free_node
(p
, subtype
(p
));
1346 case local_par_node
:
1351 if
(w
>= backend_first_pdf_whatsit
) {
1352 flush_node_wrapup_pdf
(p
);
1353 } else if
(w
>= backend_first_dvi_whatsit
) {
1354 flush_node_wrapup_dvi
(p
);
1356 flush_node_wrapup_core
(p
);
1360 flush_node_list
(ins_ptr
(p
));
1362 case margin_kern_node
:
1363 flush_node
(margin_char
(p
));
1366 delete_token_ref
(mark_ptr
(p
));
1369 flush_node_list
(adjust_ptr
(p
));
1371 case style_node
: /* nothing to do
*/
1374 free_sub_list
(display_mlist
(p
));
1375 free_sub_list
(text_mlist
(p
));
1376 free_sub_list
(script_mlist
(p
));
1377 free_sub_list
(script_script_mlist
(p
));
1380 free_sub_list
(nucleus
(p
));
1381 free_sub_list
(subscr
(p
));
1382 free_sub_list
(supscr
(p
));
1385 free_sub_list
(nucleus
(p
));
1386 free_sub_list
(subscr
(p
));
1387 free_sub_list
(supscr
(p
));
1388 free_sub_node
(left_delimiter
(p
));
1389 free_sub_list
(degree
(p
));
1392 free_sub_list
(nucleus
(p
));
1393 free_sub_list
(subscr
(p
));
1394 free_sub_list
(supscr
(p
));
1395 free_sub_list
(top_accent_chr
(p
));
1396 free_sub_list
(bot_accent_chr
(p
));
1397 free_sub_list
(overlay_accent_chr
(p
));
1400 free_sub_list
(delimiter
(p
));
1402 case delim_node
: /* nothing to do
*/
1403 case math_char_node
:
1404 case math_text_char_node
:
1407 case sub_mlist_node
:
1408 free_sub_list
(math_list
(p
));
1411 free_sub_list
(numerator
(p
));
1412 free_sub_list
(denominator
(p
));
1413 free_sub_node
(left_delimiter
(p
));
1414 free_sub_node
(right_delimiter
(p
));
1416 case pseudo_file_node
:
1417 free_sub_list
(pseudo_lines
(p
));
1419 case pseudo_line_node
:
1421 free_node
(p
, subtype
(p
));
1424 case align_stack_node
:
1429 case unhyphenated_node
:
1430 case hyphenated_node
:
1433 case inserting_node
:
1436 case attribute_node
:
1437 case attribute_list_node
:
1441 formatted_error
("nodes","flushing weird node type %d", type
(p
));
1444 if
(nodetype_has_attributes
(type
(p
))) {
1445 delete_attribute_ref
(node_attr
(p
));
1446 lua_properties_reset
(p
);
1448 free_node
(p
, get_node_size
(type
(p
), subtype
(p
)));
1453 void flush_node_list
(halfword pp
)
1454 { /* erase list of nodes starting at |p|
*/
1455 register halfword p
= pp
;
1456 if
(p
== null
) /* legal
, but no-op
*/
1460 lua_properties_push
; /* saves stack and time
*/
1462 register halfword q
= vlink
(p
);
1466 lua_properties_pop
; /* saves stack and time
*/
1470 static void check_node_wrapup_core
(halfword p
)
1472 switch
(subtype
(p
)) {
1475 check_token_ref
(write_tokens
(p
));
1477 case user_defined_node
:
1478 switch
(user_node_type
(p
)) {
1480 check_attribute_ref
(user_node_value
(p
));
1483 check_token_ref
(user_node_value
(p
));
1486 dorangetest
(p
, user_node_value
(p
), var_mem_max
);
1492 confusion
("unknown user node type");
1504 void check_node_wrapup_dvi
(halfword p
)
1508 void check_node_wrapup_pdf
(halfword p
)
1510 switch
(subtype
(p
)) {
1511 case pdf_literal_node
:
1512 if
(pdf_literal_type
(p
) == normal
)
1513 check_token_ref
(pdf_literal_data
(p
));
1515 case pdf_colorstack_node
:
1516 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
1517 check_token_ref
(pdf_colorstack_data
(p
));
1519 case pdf_setmatrix_node
:
1520 check_token_ref
(pdf_setmatrix_data
(p
));
1523 if
(late_lua_name
(p
) > 0)
1524 check_token_ref
(late_lua_name
(p
));
1525 if
(late_lua_type
(p
) == normal
)
1526 check_token_ref
(late_lua_data
(p
));
1528 case pdf_annot_node
:
1529 check_token_ref
(pdf_annot_data
(p
));
1531 case pdf_start_link_node
:
1532 if
(pdf_link_attr
(p
) != null
)
1533 check_token_ref
(pdf_link_attr
(p
));
1534 check_action_ref
(pdf_link_action
(p
));
1537 if
(pdf_dest_named_id
(p
) > 0)
1538 check_token_ref
(pdf_dest_id
(p
));
1540 case pdf_thread_node
:
1541 case pdf_start_thread_node
:
1542 if
(pdf_thread_named_id
(p
) > 0)
1543 check_token_ref
(pdf_thread_id
(p
));
1544 if
(pdf_thread_attr
(p
) != null
)
1545 check_token_ref
(pdf_thread_attr
(p
));
1548 case pdf_restore_node
:
1549 case pdf_refobj_node
:
1550 case pdf_end_link_node
:
1551 case pdf_end_thread_node
:
1554 confusion
("wrapup pdf nodes");
1559 void check_node
(halfword p
)
1564 dorangetest
(p
, lig_ptr
(p
), var_mem_max
);
1567 dorangetest
(p
, leader_ptr
(p
), var_mem_max
);
1572 case align_record_node
:
1573 dorangetest
(p
, list_ptr
(p
), var_mem_max
);
1576 dorangetest
(p
, ins_ptr
(p
), var_mem_max
);
1580 if
(w
>= backend_first_pdf_whatsit
) {
1581 check_node_wrapup_pdf
(p
);
1582 } else if
(w
>= backend_first_dvi_whatsit
) {
1583 check_node_wrapup_dvi
(p
);
1585 check_node_wrapup_core
(p
);
1588 case margin_kern_node
:
1589 check_node
(margin_char
(p
));
1594 dorangetest
(p
, vlink
(pre_break
(p
)), var_mem_max
);
1595 dorangetest
(p
, vlink
(post_break
(p
)), var_mem_max
);
1596 dorangetest
(p
, vlink
(no_break
(p
)), var_mem_max
);
1599 dorangetest
(p
, adjust_ptr
(p
), var_mem_max
);
1601 case pseudo_file_node
:
1602 dorangetest
(p
, pseudo_lines
(p
), var_mem_max
);
1604 case pseudo_line_node
:
1608 dorangetest
(p
, display_mlist
(p
), var_mem_max
);
1609 dorangetest
(p
, text_mlist
(p
), var_mem_max
);
1610 dorangetest
(p
, script_mlist
(p
), var_mem_max
);
1611 dorangetest
(p
, script_script_mlist
(p
), var_mem_max
);
1614 dorangetest
(p
, numerator
(p
), var_mem_max
);
1615 dorangetest
(p
, denominator
(p
), var_mem_max
);
1616 dorangetest
(p
, left_delimiter
(p
), var_mem_max
);
1617 dorangetest
(p
, right_delimiter
(p
), var_mem_max
);
1620 dorangetest
(p
, nucleus
(p
), var_mem_max
);
1621 dorangetest
(p
, subscr
(p
), var_mem_max
);
1622 dorangetest
(p
, supscr
(p
), var_mem_max
);
1625 dorangetest
(p
, nucleus
(p
), var_mem_max
);
1626 dorangetest
(p
, subscr
(p
), var_mem_max
);
1627 dorangetest
(p
, supscr
(p
), var_mem_max
);
1628 dorangetest
(p
, degree
(p
), var_mem_max
);
1629 dorangetest
(p
, left_delimiter
(p
), var_mem_max
);
1632 dorangetest
(p
, nucleus
(p
), var_mem_max
);
1633 dorangetest
(p
, subscr
(p
), var_mem_max
);
1634 dorangetest
(p
, supscr
(p
), var_mem_max
);
1635 dorangetest
(p
, top_accent_chr
(p
), var_mem_max
);
1636 dorangetest
(p
, bot_accent_chr
(p
), var_mem_max
);
1637 dorangetest
(p
, overlay_accent_chr
(p
), var_mem_max
);
1640 dorangetest
(p
, delimiter
(p
), var_mem_max
);
1648 case attribute_list_node
:
1649 case attribute_node
:
1650 case glue_spec_node
:
1652 case align_stack_node
:
1657 case unhyphenated_node
:
1658 case hyphenated_node
:
1664 case local_par_node
:
1667 fprintf
(stdout
, "check_node: type is %d\n", type
(p
));
1673 void fix_node_list
(halfword head
)
1688 halfword get_node
(int s
)
1690 register halfword r
;
1692 if
(s
< MAX_CHAIN_SIZE
) {
1695 free_chain
[s
] = vlink
(r
);
1696 #ifdef CHECK_NODE_USAGE
1697 varmem_sizes
[r
] = (char
) s
;
1700 var_used
+= s
; /* maintain usage statistics
*/
1703 /* this is the end of the 'inner loop'
*/
1704 return slow_get_node
(s
);
1706 normal_error
("nodes","there is a problem in getting a node, case 1");
1712 void free_node
(halfword p
, int s
)
1714 if
(p
<= my_prealloc
) {
1715 formatted_error
("nodes", "node number %d of type %d should not be freed", (int
) p
, type
(p
));
1718 #ifdef CHECK_NODE_USAGE
1719 varmem_sizes
[p
] = 0;
1721 if
(s
< MAX_CHAIN_SIZE
) {
1722 vlink
(p
) = free_chain
[s
];
1725 /* todo ? it is perhaps possible to merge this node with an existing rover
*/
1728 while
(vlink
(rover
) != vlink
(p
)) {
1729 rover
= vlink
(rover
);
1733 /* maintain statistics
*/
1738 static void free_node_chain
(halfword q
, int s
)
1740 register halfword p
= q
;
1741 while
(vlink
(p
) != null
) {
1742 #ifdef CHECK_NODE_USAGE
1743 varmem_sizes
[p
] = 0;
1749 #ifdef CHECK_NODE_USAGE
1750 varmem_sizes
[p
] = 0;
1752 vlink
(p
) = free_chain
[s
];
1757 void init_node_mem
(int t
)
1759 my_prealloc
= var_mem_stat_max
;
1763 assert
(whatsit_node_data
[user_defined_node
].id
== user_defined_node
);
1764 assert
(node_data
[passive_node
].id
== passive_node
);
1767 varmem
= (memory_word
*) realloc
((void
*) varmem
, sizeof
(memory_word
) * (unsigned
) t
);
1768 if
(varmem
== NULL) {
1769 overflow
("node memory size", (unsigned
) var_mem_max
);
1771 memset
((void
*) (varmem
), 0, (unsigned
) t
* sizeof
(memory_word
));
1772 #ifdef CHECK_NODE_USAGE
1773 varmem_sizes
= (char
*) realloc
(varmem_sizes
, sizeof
(char
) * (unsigned
) t
);
1774 if
(varmem_sizes
== NULL) {
1775 overflow
("node memory size", (unsigned
) var_mem_max
);
1777 memset
((void
*) varmem_sizes
, 0, sizeof
(char
) * (unsigned
) t
);
1780 rover
= var_mem_stat_max
+ 1;
1781 vlink
(rover
) = rover
;
1782 node_size
(rover
) = (t
- rover
);
1784 /* initialize static glue specs
*/
1785 width
(zero_glue
) = 0;
1786 type
(zero_glue
) = glue_spec_node
;
1787 vlink
(zero_glue
) = null
;
1788 stretch
(zero_glue
) = 0;
1789 stretch_order
(zero_glue
) = normal
;
1790 shrink
(zero_glue
) = 0;
1791 shrink_order
(zero_glue
) = normal
;
1792 width
(sfi_glue
) = 0;
1793 type
(sfi_glue
) = glue_spec_node
;
1794 vlink
(sfi_glue
) = null
;
1795 stretch
(sfi_glue
) = 0;
1796 stretch_order
(sfi_glue
) = sfi
;
1797 shrink
(sfi_glue
) = 0;
1798 shrink_order
(sfi_glue
) = normal
;
1799 width
(fil_glue
) = 0;
1800 type
(fil_glue
) = glue_spec_node
;
1801 vlink
(fil_glue
) = null
;
1802 stretch
(fil_glue
) = unity
;
1803 stretch_order
(fil_glue
) = fil
;
1804 shrink
(fil_glue
) = 0;
1805 shrink_order
(fil_glue
) = normal
;
1806 width
(fill_glue
) = 0;
1807 type
(fill_glue
) = glue_spec_node
;
1808 vlink
(fill_glue
) = null
;
1809 stretch
(fill_glue
) = unity
;
1810 stretch_order
(fill_glue
) = fill
;
1811 shrink
(fill_glue
) = 0;
1812 shrink_order
(fill_glue
) = normal
;
1814 type
(ss_glue
) = glue_spec_node
;
1815 vlink
(ss_glue
) = null
;
1816 stretch
(ss_glue
) = unity
;
1817 stretch_order
(ss_glue
) = fil
;
1818 shrink
(ss_glue
) = unity
;
1819 shrink_order
(ss_glue
) = fil
;
1820 width
(fil_neg_glue
) = 0;
1821 type
(fil_neg_glue
) = glue_spec_node
;
1822 vlink
(fil_neg_glue
) = null
;
1823 stretch
(fil_neg_glue
) = -unity
;
1824 stretch_order
(fil_neg_glue
) = fil
;
1825 shrink
(fil_neg_glue
) = 0;
1826 shrink_order
(fil_neg_glue
) = normal
;
1827 /* initialize node list heads
*/
1828 vinfo
(page_ins_head
) = 0;
1829 type
(page_ins_head
) = temp_node
;
1830 vlink
(page_ins_head
) = null
;
1831 alink
(page_ins_head
) = null
;
1832 vinfo
(contrib_head
) = 0;
1833 type
(contrib_head
) = temp_node
;
1834 vlink
(contrib_head
) = null
;
1835 alink
(contrib_head
) = null
;
1836 vinfo
(page_head
) = 0;
1837 type
(page_head
) = temp_node
;
1838 vlink
(page_head
) = null
;
1839 alink
(page_head
) = null
;
1840 vinfo
(temp_head
) = 0;
1841 type
(temp_head
) = temp_node
;
1842 vlink
(temp_head
) = null
;
1843 alink
(temp_head
) = null
;
1844 vinfo
(hold_head
) = 0;
1845 type
(hold_head
) = temp_node
;
1846 vlink
(hold_head
) = null
;
1847 alink
(hold_head
) = null
;
1848 vinfo
(adjust_head
) = 0;
1849 type
(adjust_head
) = temp_node
;
1850 vlink
(adjust_head
) = null
;
1851 alink
(adjust_head
) = null
;
1852 vinfo
(pre_adjust_head
) = 0;
1853 type
(pre_adjust_head
) = temp_node
;
1854 vlink
(pre_adjust_head
) = null
;
1855 alink
(pre_adjust_head
) = null
;
1857 type
(active
) = unhyphenated_node
;
1858 vlink
(active
) = null
;
1859 alink
(active
) = null
;
1860 vinfo
(align_head
) = 0;
1861 type
(align_head
) = temp_node
;
1862 vlink
(align_head
) = null
;
1863 alink
(align_head
) = null
;
1864 vinfo
(end_span
) = 0;
1865 type
(end_span
) = span_node
;
1866 vlink
(end_span
) = null
;
1867 alink
(end_span
) = null
;
1868 type
(begin_point
) = glyph_node
;
1869 subtype
(begin_point
) = 0;
1870 vlink
(begin_point
) = null
;
1871 vinfo
(begin_point
+ 1) = null
;
1872 alink
(begin_point
) = null
;
1873 font
(begin_point
) = 0;
1874 character
(begin_point
) = '.'
;
1875 vinfo
(begin_point
+ 3) = 0;
1876 vlink
(begin_point
+ 3) = 0;
1877 vinfo
(begin_point
+ 4) = 0;
1878 vlink
(begin_point
+ 4) = 0;
1879 type
(end_point
) = glyph_node
;
1880 subtype
(end_point
) = 0;
1881 vlink
(end_point
) = null
;
1882 vinfo
(end_point
+ 1) = null
;
1883 alink
(end_point
) = null
;
1884 font
(end_point
) = 0;
1885 character
(end_point
) = '.'
;
1886 vinfo
(end_point
+ 3) = 0;
1887 vlink
(end_point
+ 3) = 0;
1888 vinfo
(end_point
+ 4) = 0;
1889 vlink
(end_point
+ 4) = 0;
1893 void dump_node_mem
(void
)
1895 dump_int
(var_mem_max
);
1897 dump_things
(varmem
[0], var_mem_max
);
1898 #ifdef CHECK_NODE_USAGE
1899 dump_things
(varmem_sizes
[0], var_mem_max
);
1901 dump_things
(free_chain
[0], MAX_CHAIN_SIZE
);
1903 dump_int
(my_prealloc
);
1906 @ it makes sense to enlarge the varmem array immediately
1909 void undump_node_mem
(void
)
1914 var_mem_max
= (x
< 100000 ?
100000 : x
);
1915 varmem
= xmallocarray
(memory_word
, (unsigned
) var_mem_max
);
1916 undump_things
(varmem
[0], x
);
1917 #ifdef CHECK_NODE_USAGE
1918 varmem_sizes
= xmallocarray
(char
, (unsigned
) var_mem_max
);
1919 memset
((void
*) varmem_sizes
, 0, (unsigned
) var_mem_max
* sizeof
(char
));
1920 undump_things
(varmem_sizes
[0], x
);
1922 undump_things
(free_chain
[0], MAX_CHAIN_SIZE
);
1923 undump_int
(var_used
);
1924 undump_int
(my_prealloc
);
1925 if
(var_mem_max
> x
) {
1926 /* todo ? it is perhaps possible to merge the new node with an existing rover
*/
1928 node_size
(x
) = (var_mem_max
- x
);
1929 while
(vlink
(rover
) != vlink
(x
)) {
1930 rover
= vlink
(rover
);
1937 halfword slow_get_node
(int s
)
1942 t
= node_size
(rover
);
1943 if
(vlink
(rover
) < var_mem_max
&& vlink(rover) != 0) {
1945 /* allocating from the bottom helps decrease page faults
*/
1946 register halfword r
= rover
;
1948 vlink
(rover
) = vlink
(r
);
1949 node_size
(rover
) = node_size
(r
) - s
;
1950 if
(vlink
(rover
) != r
) { /* list is longer than one
*/
1952 while
(vlink
(q
) != r
) {
1959 if
(vlink
(rover
) < var_mem_max
) {
1960 #ifdef CHECK_NODE_USAGE
1961 varmem_sizes
[r
] = (char
) (s
> 127 ?
127 : s
);
1964 var_used
+= s
; /* maintain usage statistics
*/
1965 return r
; /* this is the only exit
*/
1967 normal_error
("nodes","there is a problem in getting a node, case 2");
1971 /* attempt to keep the free list small
*/
1973 if
(vlink
(rover
) != rover
) {
1974 if
(t
< MAX_CHAIN_SIZE
) {
1975 halfword l
= vlink
(rover
);
1976 vlink
(rover
) = free_chain
[t
];
1977 free_chain
[t
] = rover
;
1979 while
(vlink
(l
) != free_chain
[t
]) {
1986 while
(vlink
(rover
) != l
) {
1987 if
(node_size
(rover
) > s
) {
1990 rover
= vlink
(rover
);
1994 /* if we are still here
, it was apparently impossible to get a match
*/
1995 x
= (var_mem_max
>> 2) + s
;
1996 varmem
= (memory_word
*) realloc
((void
*) varmem
, sizeof
(memory_word
) * (unsigned
) (var_mem_max
+ x
));
1997 if
(varmem
== NULL) {
1998 overflow
("node memory size", (unsigned
) var_mem_max
);
2000 memset
((void
*) (varmem
+ var_mem_max
), 0, (unsigned
) x
* sizeof
(memory_word
));
2001 #ifdef CHECK_NODE_USAGE
2002 varmem_sizes
= (char
*) realloc
(varmem_sizes
, sizeof
(char
) * (unsigned
) (var_mem_max
+ x
));
2003 if
(varmem_sizes
== NULL) {
2004 overflow
("node memory size", (unsigned
) var_mem_max
);
2006 memset
((void
*) (varmem_sizes
+ var_mem_max
), 0, (unsigned
) (x
) * sizeof
(char
));
2008 /* todo ? it is perhaps possible to merge the new memory with an existing rover
*/
2009 vlink
(var_mem_max
) = rover
;
2010 node_size
(var_mem_max
) = x
;
2011 while
(vlink
(rover
) != vlink
(var_mem_max
)) {
2012 rover
= vlink
(rover
);
2014 vlink
(rover
) = var_mem_max
;
2015 rover
= var_mem_max
;
2020 normal_error
("nodes","there is a problem in getting a node, case 3");
2026 char
*sprint_node_mem_usage
(void
)
2029 #ifdef CHECK_NODE_USAGE
2034 int node_counts
[last_normal_node
+ last_whatsit_node
+ 2] = { 0 };
2036 for
(i
= (var_mem_max
- 1); i
> my_prealloc
; i--
) {
2037 if
(varmem_sizes
[i
] > 0) {
2038 if
(type
(i
) > last_normal_node
+ last_whatsit_node
) {
2039 node_counts
[last_normal_node
+ last_whatsit_node
+ 1]++;
2040 } else if
(type
(i
) == whatsit_node
) {
2041 node_counts
[(subtype
(i
) + last_normal_node
+ 1)]++;
2043 node_counts
[type
(i
)]++;
2047 for
(i
= 0; i
< last_normal_node
+ last_whatsit_node
+ 2; i
++) {
2048 if
(node_counts
[i
] > 0) {
2050 (i
> (last_normal_node
+ 1) ?
(i
- last_normal_node
- 1) : 0);
2051 snprintf
(msg
, 255, "%s%d %s", (b ?
", " : ""), (int
) node_counts
[i
],
2052 get_node_name
((i
> last_normal_node ? whatsit_node
: i
), j
));
2053 ss
= xmalloc
((unsigned
) (strlen
(s
) + strlen
(msg
) + 1));
2068 halfword list_node_mem_usage
(void
)
2071 #ifdef CHECK_NODE_USAGE
2074 char
*saved_varmem_sizes
= xmallocarray
(char
, (unsigned
) var_mem_max
);
2075 memcpy
(saved_varmem_sizes
, varmem_sizes
, (size_t
) var_mem_max
);
2076 for
(i
= my_prealloc
+ 1; i
< (var_mem_max
- 1); i
++) {
2077 if
(saved_varmem_sizes
[i
] > 0) {
2087 free
(saved_varmem_sizes
);
2093 void print_node_mem_stats
(void
)
2099 int free_chain_counts
[MAX_CHAIN_SIZE
] = { 0 };
2100 snprintf
(msg
, 255, " %d words of node memory still in use:", (int
) (var_used
+ my_prealloc
));
2102 s
= sprint_node_mem_usage
();
2107 tprint_nl
(" avail lists: ");
2109 for
(i
= 1; i
< MAX_CHAIN_SIZE
; i
++) {
2110 for
(j
= free_chain
[i
]; j
!= null
; j
= vlink
(j
))
2111 free_chain_counts
[i
]++;
2112 if
(free_chain_counts
[i
] > 0) {
2113 snprintf
(msg
, 255, "%s%d:%d", (b ?
"," : ""), i
, (int
) free_chain_counts
[i
]);
2118 /* newline
, if needed
*/
2122 /* this belongs in the web but i couldn't find the correct syntactic place
*/
2124 halfword new_span_node
(halfword n
, int s
, scaled w
)
2126 halfword p
= new_node
(span_node
, 0);
2136 static halfword new_attribute_node
(unsigned int i
, int v
)
2138 register halfword r
= get_node
(attribute_node_size
);
2139 type
(r
) = attribute_node
;
2140 attribute_id
(r
) = (halfword
) i
;
2141 attribute_value
(r
) = v
;
2142 /* not used but nicer in print
*/
2148 halfword copy_attribute_list
(halfword n
)
2150 halfword q
= get_node
(attribute_node_size
);
2151 register halfword p
= q
;
2152 type
(p
) = attribute_list_node
;
2153 attr_list_ref
(p
) = 0;
2156 register halfword r
= get_node
(attribute_node_size
);
2157 /* the link will be fixed automatically in the next loop
*/
2158 (void
) memcpy
((void
*) (varmem
+ r
), (void
*) (varmem
+ n
),
2159 (sizeof
(memory_word
) * attribute_node_size
));
2168 void update_attribute_cache
(void
)
2172 attr_list_cache
= get_node
(attribute_node_size
);
2173 type
(attr_list_cache
) = attribute_list_node
;
2174 attr_list_ref
(attr_list_cache
) = 0;
2175 p
= attr_list_cache
;
2176 for
(i
= 0; i
<= max_used_attr
; i
++) {
2177 register int v
= attribute
(i
);
2178 if
(v
> UNUSED_ATTRIBUTE
) {
2179 register halfword r
= new_attribute_node
((unsigned
) i
, v
);
2184 if
(vlink
(attr_list_cache
) == null
) {
2185 free_node
(attr_list_cache
, attribute_node_size
);
2186 attr_list_cache
= null
;
2192 void build_attribute_list
(halfword b
)
2194 if
(max_used_attr
>= 0) {
2195 if
(attr_list_cache
== cache_disabled|| attr_list_cache
== null
) {
2196 update_attribute_cache
();
2197 if
(attr_list_cache
== null
)
2200 attr_list_ref
(attr_list_cache
)++;
2201 node_attr
(b
) = attr_list_cache
;
2206 halfword current_attribute_list
(void
)
2208 if
(max_used_attr
>= 0) {
2209 if
(attr_list_cache
== cache_disabled
) {
2210 update_attribute_cache
();
2212 return attr_list_cache
;
2219 void reassign_attribute
(halfword n
, halfword new
)
2224 /* there is nothing to assign but we need to check for an old value
*/
2226 delete_attribute_ref
(old
); /* also nulls attr field of n
*/
2227 } else if
(old
== null
) {
2228 /* nothing is assigned so we just do that now
*/
2229 assign_attribute_ref
(n
,new
);
2230 } else if
(old
!= new
) {
2231 /* something is assigned so we need to clean up and assign then
*/
2232 delete_attribute_ref
(old
);
2233 assign_attribute_ref
(n
,new
);
2235 /* else
: same value so there is no need to assign and change the refcount
*/
2236 node_attr
(n
) = new
;
2240 void delete_attribute_ref
(halfword b
)
2243 if
(type
(b
) == attribute_list_node
){
2245 if
(attr_list_ref
(b
) == 0) {
2246 if
(b
== attr_list_cache
)
2247 attr_list_cache
= cache_disabled
;
2248 free_node_chain
(b
, attribute_node_size
);
2250 /* maintain sanity
*/
2251 if
(attr_list_ref
(b
) < 0) {
2252 attr_list_ref
(b
) = 0;
2255 normal_error
("nodes","trying to delete an attribute reference of a non attribute node");
2260 void reset_node_properties
(halfword b
)
2263 lua_properties_reset
(b
);
2267 @ |p| is an attr list head
, or zero
2269 halfword do_set_attribute
(halfword p
, int i
, int val
)
2271 register halfword q
;
2273 if
(p
== null
) { /* add a new head \
& node */
2274 q
= get_node
(attribute_node_size
);
2275 type
(q
) = attribute_list_node
;
2276 attr_list_ref
(q
) = 1;
2277 p
= new_attribute_node
((unsigned
) i
, val
);
2282 if
(vlink
(p
) != null
) {
2283 while
(vlink
(p
) != null
) {
2284 int t
= attribute_id
(vlink
(p
));
2285 if
(t
== i
&& attribute_value(vlink(p)) == val)
2286 return q
; /* no need to do anything
*/
2296 if
(attribute_id
(vlink
(p
)) == i
) {
2297 attribute_value
(vlink
(p
)) = val
;
2298 } else
{ /* add a new node
*/
2299 halfword r
= new_attribute_node
((unsigned
) i
, val
);
2300 vlink
(r
) = vlink
(p
);
2305 normal_error
("nodes","trying to set an attribute fails, case 1");
2311 void set_attribute
(halfword n
, int i
, int val
)
2313 register halfword p
;
2315 /* not all nodes can have an attribute list
*/
2316 if
(!nodetype_has_attributes
(type
(n
)))
2318 /* if we have no list
, we create one and quit
*/
2320 if
(p
== null
) { /* add a new head \
& node */
2321 p
= get_node
(attribute_node_size
);
2322 type
(p
) = attribute_list_node
;
2323 attr_list_ref
(p
) = 1;
2325 p
= new_attribute_node
((unsigned
) i
, val
);
2326 vlink
(node_attr
(n
)) = p
;
2329 /* we check if we have this attribute already and quit if the value stays the same
*/
2330 if
(vlink
(p
) != null
) {
2331 while
(vlink
(p
) != null
) {
2332 int t
= attribute_id
(vlink
(p
));
2333 if
(t
== i
&& attribute_value(vlink(p)) == val)
2340 /* j has now the position
(if found
) .. we assume a sorted list
! */
2343 if
(attr_list_ref
(p
) == 0 ) {
2344 /* the list is invalid i.e. freed already
*/
2345 formatted_warning
("nodes","node %d has an attribute list that is free already, case 1",(int
) n
);
2346 /* the still dangling list gets ref count
1 */
2347 attr_list_ref
(p
) = 1;
2348 } else if
(attr_list_ref
(p
) == 1) {
2349 /* this can really happen HH-LS
*/
2350 if
(p
== attr_list_cache
) {
2351 /* we can invalidate the cache setting
*/
2352 /* attr_list_cache
= cache_disabled
*/
2353 /* or save the list
, as done below
*/
2354 p
= copy_attribute_list
(p
);
2356 /* the copied list gets ref count
1 */
2357 attr_list_ref
(p
) = 1;
2360 /* the list is used multiple times so we make a copy
*/
2361 p
= copy_attribute_list
(p
);
2362 /* we decrement the ref count or the original
*/
2363 delete_attribute_ref
(node_attr
(n
));
2365 /* the copied list gets ref count
1 */
2366 attr_list_ref
(p
) = 1;
2370 /* we go to position j in the list
*/
2373 /* if we have a hit we just set the value otherwise we add a new node
*/
2374 if
(attribute_id
(vlink
(p
)) == i
) {
2375 attribute_value
(vlink
(p
)) = val
;
2376 } else
{ /* add a new node
*/
2377 halfword r
= new_attribute_node
((unsigned
) i
, val
);
2378 vlink
(r
) = vlink
(p
);
2382 normal_error
("nodes","trying to set an attribute fails, case 2");
2387 int unset_attribute
(halfword n
, int i
, int val
)
2389 register halfword p
;
2393 if
(!nodetype_has_attributes
(type
(n
)))
2397 return UNUSED_ATTRIBUTE
;
2398 if
(attr_list_ref
(p
) == 0) {
2399 formatted_warning
("nodes","node %d has an attribute list that is free already, case 2", (int
) n
);
2400 return UNUSED_ATTRIBUTE
;
2402 if
(vlink
(p
) != null
) {
2403 while
(vlink
(p
) != null
) {
2404 t
= attribute_id
(vlink
(p
));
2406 return UNUSED_ATTRIBUTE
;
2414 if
(attribute_id
(p
) != i
)
2415 return UNUSED_ATTRIBUTE
;
2416 /* if we are still here
, the attribute exists
*/
2418 if
(attr_list_ref
(p
) > 1 || p
== attr_list_cache
) {
2419 halfword q
= copy_attribute_list
(p
);
2420 if
(attr_list_ref
(p
) > 1) {
2421 delete_attribute_ref
(node_attr
(n
));
2423 attr_list_ref
(q
) = 1;
2426 p
= vlink
(node_attr
(n
));
2429 t
= attribute_value
(p
);
2430 if
(val
== UNUSED_ATTRIBUTE || t
== val
) {
2431 attribute_value
(p
) = UNUSED_ATTRIBUTE
;
2435 normal_error
("nodes","trying to unset an attribute fails");
2441 int has_attribute
(halfword n
, int i
, int val
)
2443 register halfword p
;
2444 if
(!nodetype_has_attributes
(type
(n
)))
2445 return UNUSED_ATTRIBUTE
;
2447 if
(p
== null || vlink
(p
) == null
)
2448 return UNUSED_ATTRIBUTE
;
2451 if
(attribute_id
(p
) == i
) {
2452 int ret
= attribute_value
(p
);
2453 if
(val
== UNUSED_ATTRIBUTE || val
== ret
)
2455 return UNUSED_ATTRIBUTE
;
2456 } else if
(attribute_id
(p
) > i
) {
2457 return UNUSED_ATTRIBUTE
;
2461 return UNUSED_ATTRIBUTE
;
2465 void print_short_node_contents
(halfword p
)
2482 if
(! glue_is_zero
(p
))
2489 short_display
(vlink
(pre_break
(p
)));
2490 short_display
(vlink
(post_break
(p
)));
2496 static void show_pdftex_whatsit_rule_spec
(int p
)
2499 print_rule_dimen
(height
(p
));
2501 print_rule_dimen
(depth
(p
));
2503 print_rule_dimen
(width
(p
));
2506 @ Each new type of node that appears in our data structure must be capable
2507 of being displayed
, copied
, destroyed
, and so on. The routines that we
2508 need for write-oriented whatsits are somewhat like those for mark nodes
;
2509 other extensions might
, of course
, involve more subtlety here.
2512 static void print_write_whatsit
(const char
*s
, pointer p
)
2515 if
(write_stream
(p
) < 16)
2516 print_int
(write_stream
(p
));
2517 else if
(write_stream
(p
) == 16)
2524 static void show_node_wrapup_core
(int p
)
2526 switch
(subtype
(p
)) {
2528 print_write_whatsit
("openout", p
);
2530 print_file_name
(open_name
(p
), open_area
(p
), open_ext
(p
));
2533 print_write_whatsit
("write", p
);
2534 print_mark
(write_tokens
(p
));
2537 print_write_whatsit
("closeout", p
);
2540 tprint_esc
("special");
2541 print_mark
(write_tokens
(p
));
2547 tprint_esc
("savepos");
2549 case user_defined_node
:
2550 tprint_esc
("whatsit");
2551 print_int
(user_node_id
(p
));
2553 switch
(user_node_type
(p
)) {
2559 show_node_list
(user_node_value
(p
));
2564 print(user_node_value(p));
2568 print_mark
(user_node_value
(p
));
2570 default
: /* only 'd'
*/
2571 print_int
(user_node_value
(p
));
2578 void show_node_wrapup_dvi
(int p
)
2582 void show_node_wrapup_pdf
(int p
)
2584 switch
(subtype
(p
)) {
2585 case pdf_literal_node
:
2586 show_pdf_literal
(p
);
2588 case pdf_colorstack_node
:
2589 tprint_esc
("pdfcolorstack ");
2590 print_int
(pdf_colorstack_stack
(p
));
2591 switch
(pdf_colorstack_cmd
(p
)) {
2592 case colorstack_set
:
2595 case colorstack_push
:
2598 case colorstack_pop
:
2601 case colorstack_current
:
2605 confusion
("colorstack");
2608 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
2609 print_mark
(pdf_colorstack_data
(p
));
2611 case pdf_setmatrix_node
:
2612 tprint_esc
("pdfsetmatrix");
2613 print_mark
(pdf_setmatrix_data
(p
));
2616 tprint_esc
("pdfsave");
2618 case pdf_restore_node
:
2619 tprint_esc
("pdfrestore");
2621 case pdf_refobj_node
:
2622 tprint_esc
("pdfrefobj");
2623 if
(obj_obj_is_stream
(static_pdf
, pdf_obj_objnum
(p
))) {
2624 if
(obj_obj_stream_attr
(static_pdf
, pdf_obj_objnum
(p
)) != LUA_NOREF
) {
2626 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
,
2627 obj_obj_stream_attr
(static_pdf
, pdf_obj_objnum
(p
)));
2629 tprint
((const char
*) lua_tostring
(Luas
, -1));
2634 if
(obj_obj_is_file
(static_pdf
, pdf_obj_objnum
(p
)))
2636 if
(obj_obj_data
(static_pdf
, pdf_obj_objnum
(p
)) != LUA_NOREF
) {
2637 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
,
2638 obj_obj_data
(static_pdf
, pdf_obj_objnum
(p
)));
2640 tprint
((const char
*) lua_tostring
(Luas
, -1));
2644 case pdf_annot_node
:
2645 tprint_esc
("pdfannot");
2646 show_pdftex_whatsit_rule_spec
(p
);
2647 print_mark
(pdf_annot_data
(p
));
2649 case pdf_start_link_node
:
2650 tprint_esc
("pdfstartlink");
2651 show_pdftex_whatsit_rule_spec
(p
);
2652 if
(pdf_link_attr
(p
) != null
) {
2654 print_mark
(pdf_link_attr
(p
));
2657 if
(pdf_action_type
(pdf_link_action
(p
)) == pdf_action_user
) {
2659 print_mark
(pdf_action_tokens
(pdf_link_action
(p
)));
2662 if
(pdf_action_file
(pdf_link_action
(p
)) != null
) {
2664 print_mark
(pdf_action_file
(pdf_link_action
(p
)));
2666 switch
(pdf_action_type
(pdf_link_action
(p
))) {
2667 case pdf_action_goto
:
2668 if
(pdf_action_named_id
(pdf_link_action
(p
)) > 0) {
2669 tprint
(" goto name");
2670 print_mark
(pdf_action_id
(pdf_link_action
(p
)));
2672 tprint
(" goto num");
2673 print_int
(pdf_action_id
(pdf_link_action
(p
)));
2676 case pdf_action_page
:
2678 print_int
(pdf_action_id
(pdf_link_action
(p
)));
2679 print_mark
(pdf_action_tokens
(pdf_link_action
(p
)));
2681 case pdf_action_thread
:
2682 if
(pdf_action_named_id
(pdf_link_action
(p
)) > 0) {
2683 tprint
(" thread name");
2684 print_mark
(pdf_action_id
(pdf_link_action
(p
)));
2686 tprint
(" thread num");
2687 print_int
(pdf_action_id
(pdf_link_action
(p
)));
2691 normal_error
("pdf backend", "unknown action type for link");
2695 case pdf_end_link_node
:
2696 tprint_esc
("pdfendlink");
2699 tprint_esc
("pdfdest");
2700 if
(pdf_dest_named_id
(p
) > 0) {
2702 print_mark
(pdf_dest_id
(p
));
2705 print_int
(pdf_dest_id
(p
));
2708 switch
(pdf_dest_type
(p
)) {
2711 if
(pdf_dest_xyz_zoom
(p
) != null
) {
2713 print_int
(pdf_dest_xyz_zoom
(p
));
2716 case pdf_dest_fitbh
:
2719 case pdf_dest_fitbv
:
2733 show_pdftex_whatsit_rule_spec
(p
);
2743 case pdf_thread_node
:
2744 case pdf_start_thread_node
:
2745 if
(subtype
(p
) == pdf_thread_node
)
2746 tprint_esc
("pdfthread");
2748 tprint_esc
("pdfstartthread");
2750 print_rule_dimen
(height
(p
));
2752 print_rule_dimen
(depth
(p
));
2754 print_rule_dimen
(width
(p
));
2755 if
(pdf_thread_attr
(p
) != null
) {
2757 print_mark
(pdf_thread_attr
(p
));
2759 if
(pdf_thread_named_id
(p
) > 0) {
2761 print_mark
(pdf_thread_id
(p
));
2764 print_int
(pdf_thread_id
(p
));
2767 case pdf_end_thread_node
:
2768 tprint_esc
("pdfendthread");
2775 @ Now we are ready for |show_node_list| itself. This procedure has been
2776 written to be ``extra robust'' in the sense that it should not crash or get
2777 into a loop even if the data structures have been messed up by bugs in
2778 the rest of the program. You can safely call its parent routine
2779 |show_box
(p
)| for arbitrary values of |p| when you are debugging \TeX.
2780 However
, in the presence of bad data
, the procedure may
2781 fetch a |memory_word| whose variant is different from the way it was stored
;
2782 for example
, it might try to read |mem
[p
].hh| when |mem
[p
]|
2783 contains a scaled integer
, if |p| is a pointer that has been
2784 clobbered or chosen at random.
2787 @ |str_room| need not be checked
; see |show_box|
2789 @ Recursive calls on |show_node_list| therefore use the following pattern
:
2791 #define node_list_display
(A
) do
{ \
2793 show_node_list
(A
); \
2797 /* prints a node list symbolically
*/
2799 void show_node_list
(int p
)
2801 int n
= 0; /* the number of items already printed at this level
*/
2803 real g
; /* a glue ratio
, as a floating point number
*/
2804 if
((int
) cur_length
> depth_threshold
) {
2806 tprint
(" []"); /* indicate that there's been some truncation
*/
2811 print_current_string
(); /* display the nesting history
*/
2812 if
(int_par
(tracing_online_code
) < -2)
2815 if
(n
> breadth_max
) { /* time to stop
*/
2819 /* Display node |p|
*/
2820 if
(is_char_node
(p
)) {
2821 print_font_and_char
(p
);
2822 if
(is_ligature
(p
)) {
2823 /* Display ligature |p|
; */
2824 tprint
(" (ligature ");
2825 if
(is_leftboundary
(p
))
2827 font_in_short_display
= font
(p
);
2828 short_display
(lig_ptr
(p
));
2829 if
(is_rightboundary
(p
))
2838 /* Display box |p|
; */
2839 if
(type
(p
) == hlist_node
)
2841 else if
(type
(p
) == vlist_node
)
2844 tprint_esc
("unset");
2846 print_scaled
(height
(p
));
2848 print_scaled
(depth
(p
));
2850 print_scaled
(width
(p
));
2851 if
(type
(p
) == unset_node
) {
2852 /* Display special fields of the unset node |p|
; */
2853 if
(span_count
(p
) != min_quarterword
) {
2855 print_int
(span_count
(p
) + 1);
2856 tprint
(" columns)");
2858 if
(glue_stretch
(p
) != 0) {
2859 tprint
(", stretch ");
2860 print_glue
(glue_stretch
(p
), glue_order
(p
), NULL);
2862 if
(glue_shrink
(p
) != 0) {
2863 tprint
(", shrink ");
2864 print_glue
(glue_shrink
(p
), glue_sign
(p
), NULL);
2867 /* Display the value of |glue_set
(p
)|
*/
2868 /* The code will have to change in this place if |glue_ratio| is
2869 a structured type instead of an ordinary |real|. Note that this routine
2870 should avoid arithmetic errors even if the |glue_set| field holds an
2871 arbitrary random value. The following code assumes that a properly
2872 formed nonzero |real| number has absolute value $
2^
{20}$ or more when
2873 it is regarded as an integer
; this precaution was adequate to prevent
2874 floating point underflow on the author's computer.
2877 g
= (real
) (glue_set
(p
));
2878 if
((g
!= 0.0) && (glue_sign(p) != normal)) {
2879 tprint
(", glue set ");
2880 if
(glue_sign
(p
) == shrinking
)
2882 if
(g
> 20000.0 || g
< -20000.0) {
2887 print_glue
(20000 * unity
, glue_order
(p
), NULL);
2889 print_glue
(round
(unity
* g
), glue_order
(p
), NULL);
2893 if
(shift_amount
(p
) != 0) {
2894 tprint
(", shifted ");
2895 print_scaled
(shift_amount
(p
));
2897 tprint
(", direction ");
2898 print_dir
(box_dir
(p
));
2900 node_list_display
(list_ptr
(p
)); /* recursive call
*/
2903 /* Display rule |p|
; */
2904 if
(subtype
(p
) == normal_rule
) {
2905 tprint_esc
("rule(");
2906 } else if
(subtype
(p
) == empty_rule
) {
2907 tprint_esc
("norule(");
2908 } else if
(subtype
(p
) == user_rule
) {
2909 tprint_esc
("userrule(");
2910 } else if
(subtype
(p
) == box_rule
) {
2912 } else if
(subtype
(p
) == image_rule
) {
2913 tprint_esc
("image(");
2915 print_rule_dimen
(height
(p
));
2917 print_rule_dimen
(depth
(p
));
2919 print_rule_dimen
(width
(p
));
2922 /* Display insertion |p|
; */
2923 tprint_esc
("insert");
2924 print_int
(subtype
(p
));
2925 tprint
(", natural size ");
2926 print_scaled
(height
(p
));
2928 print_spec
(split_top_ptr
(p
), NULL);
2930 print_scaled
(depth
(p
));
2931 tprint
("); float cost ");
2932 print_int
(float_cost
(p
));
2933 node_list_display
(ins_ptr
(p
)); /* recursive call
*/
2936 if
(dir_dir
(p
) < 0) {
2937 tprint_esc
("enddir");
2939 print_dir
(dir_dir
(p
) + dir_swap
);
2941 tprint_esc
("begindir");
2943 print_dir
(dir_dir
(p
));
2946 case local_par_node
:
2947 tprint_esc
("localpar");
2950 print_current_string
();
2951 tprint_esc
("localinterlinepenalty");
2953 print_int
(local_pen_inter
(p
));
2955 print_current_string
();
2956 tprint_esc
("localbrokenpenalty");
2958 print_int
(local_pen_broken
(p
));
2960 print_current_string
();
2961 tprint_esc
("localleftbox");
2962 if
(local_box_left
(p
) == null
) {
2966 show_node_list
(local_box_left
(p
));
2970 print_current_string
();
2971 tprint_esc
("localrightbox");
2972 if
(local_box_right
(p
) == null
) {
2976 show_node_list
(local_box_right
(p
));
2982 if
(subtype
(p
)==0) {
2983 tprint_esc
("noboundary");
2985 switch
(subtype
(p
)) {
2987 tprint_esc
("boundary");
2990 tprint_esc
("protrusionboundary");
2993 tprint_esc
("wordboundary");
2996 tprint_esc
("boundary");
2998 print_int
(subtype
(p
));
3002 print_int
(boundary_value
(p
));
3007 if
(w
>= backend_first_pdf_whatsit
) {
3008 show_node_wrapup_pdf
(p
);
3009 } else if
(w
>= backend_first_dvi_whatsit
) {
3010 show_node_wrapup_dvi
(p
);
3012 show_node_wrapup_core
(p
);
3016 /* Display glue |p|
; */
3017 if
(subtype
(p
) >= a_leaders
) {
3018 /* Display leaders |p|
; */
3020 switch
(subtype
(p
)) {
3033 normal_warning
("nodes","weird glue leader subtype ignored");
3036 print_spec
(p
, NULL);
3037 node_list_display
(leader_ptr
(p
)); /* recursive call
*/
3040 if
(subtype
(p
) != normal
) {
3042 if
((subtype
(p
) - 1) < thin_mu_skip_code
) {
3043 print_cmd_chr
(assign_glue_cmd
, glue_base
+ (subtype
(p
) - 1));
3044 } else if
(subtype
(p
) < cond_math_glue
) {
3045 print_cmd_chr
(assign_mu_glue_cmd
, glue_base
+ (subtype
(p
) - 1));
3046 } else if
(subtype
(p
) == cond_math_glue
) {
3047 tprint_esc
("nonscript");
3049 tprint_esc
("mskip");
3053 if
(subtype
(p
) != cond_math_glue
) {
3055 if
(subtype
(p
) < cond_math_glue
)
3056 print_spec
(p
, NULL);
3058 print_spec
(p
, "mu");
3062 case margin_kern_node
:
3064 print_scaled
(width
(p
));
3065 if
(subtype
(p
) == left_side
)
3066 tprint
(" (left margin)");
3068 tprint
(" (right margin)");
3071 /* Display kern |p|
; */
3072 /* An ``explicit'' kern value is indicated implicitly by an explicit space.
*/
3073 if
(subtype
(p
) != mu_glue
) {
3075 if
(subtype
(p
) != normal
)
3077 print_scaled
(width
(p
));
3078 if
(subtype
(p
) == accent_kern
)
3079 tprint
(" (for accent)");
3081 tprint_esc
("mkern");
3082 print_scaled
(width
(p
));
3087 /* Display math node |p|
; */
3089 if
(subtype
(p
) == before
)
3093 if
(!glue_is_zero
(p
)) {
3095 print_spec
(p
, NULL);
3096 } else if
(surround
(p
) != 0) {
3097 tprint
(", surrounded ");
3098 print_scaled
(surround
(p
));
3102 /* Display penalty |p|
; */
3103 tprint_esc
("penalty ");
3104 print_int
(penalty
(p
));
3107 /* Display discretionary |p|
; */
3108 /* The |post_break| list of a discretionary node is indicated by a prefixed
3109 `\.
{\char'
174}' instead of the `\..' before the |pre_break| list.
*/
3110 tprint_esc
("discretionary");
3111 print_int
(disc_penalty
(p
));
3113 if
(vlink
(no_break
(p
)) != null
) {
3114 tprint
(" replacing ");
3115 node_list_display
(vlink
(no_break
(p
)));
3117 node_list_display
(vlink
(pre_break
(p
))); /* recursive call
*/
3119 show_node_list
(vlink
(post_break
(p
)));
3120 flush_char
(); /* recursive call
*/
3123 /* Display mark |p|
; */
3125 if
(mark_class
(p
) != 0) {
3127 print_int
(mark_class
(p
));
3129 print_mark
(mark_ptr
(p
));
3132 /* Display adjustment |p|
; */
3133 tprint_esc
("vadjust");
3134 if
(subtype
(p
) != 0)
3136 node_list_display
(adjust_ptr
(p
)); /* recursive call
*/
3138 case glue_spec_node
:
3139 tprint
("<glue_spec ");
3140 print_spec
(p
, NULL);
3152 @ This routine finds the 'base' width of a horizontal box
, using the same logic
3153 that \TeX82 used for \.
{\\predisplaywidth
} */
3156 pointer actual_box_width
(pointer r
, scaled base_width
)
3158 scaled d
; /* increment to |v|
*/
3159 scaled w
= -max_dimen
; /* calculated |size|
*/
3160 scaled v
= shift_amount
(r
) + base_width
; /* |w| plus possible glue amount
*/
3161 pointer p
= list_ptr
(r
); /* current node when calculating |pre_display_size|
*/
3163 if
(is_char_node
(p
)) {
3174 case margin_kern_node
:
3181 /* begin mathskip code
*/
3182 if
(glue_is_zero
(p
)) {
3188 /* end mathskip code
*/
3190 /* We need to be careful that |w|
, |v|
, and |d| do not depend on any |glue_set|
3191 values
, since such values are subject to system-dependent rounding.
3192 System-dependent numbers are not allowed to infiltrate parameters like
3193 |pre_display_size|
, since \TeX82 is supposed to make the same decisions on all
3197 if
(glue_sign
(r
) == stretching
) {
3198 if
((glue_order
(r
) == stretch_order
(p
)) && (stretch(p) != 0))
3200 } else if
(glue_sign
(r
) == shrinking
) {
3201 if
((glue_order
(r
) == shrink_order
(p
)) && (shrink(p) != 0))
3204 if
(subtype
(p
) >= a_leaders
)
3215 if
(v
< max_dimen
) {
3229 halfword tail_of_list
(halfword p
)
3232 while
(vlink
(q
) != null
)
3242 @ Attribute lists need two extra globals to increase processing efficiency.
3243 |max_used_attr| limits the test loop that checks for set attributes
, and
3244 |attr_list_cache| contains a pointer to an already created attribute list. It is
3245 set to the special value |cache_disabled| when the current value can no longer be
3246 trusted
: after an assignment to an attribute register
, and after a group has
3250 int max_used_attr
; /* maximum assigned attribute id
*/
3251 halfword attr_list_cache
;
3253 @ From the computer's standpoint
, \TeX's chief mission is to create
3254 horizontal and vertical lists. We shall now investigate how the elements
3255 of these lists are represented internally as nodes in the dynamic memory.
3257 A horizontal or vertical list is linked together by |link| fields in
3258 the first word of each node. Individual nodes represent boxes
, glue
,
3259 penalties
, or special things like discretionary hyphens
; because of this
3260 variety
, some nodes are longer than others
, and we must distinguish different
3261 kinds of nodes. We do this by putting a `|type|' field in the first word
,
3262 together with the link and an optional `|subtype|'.
3264 @ Character nodes appear only in horizontal lists
, never in vertical lists.
3266 An |hlist_node| stands for a box that was made from a horizontal list.
3267 Each |hlist_node| is seven words long
, and contains the following fields
3268 (in addition to the mandatory |type| and |link|
, which we shall not
3269 mention explicitly when discussing the other node types
): The |height| and
3270 |width| and |depth| are scaled integers denoting the dimensions of the
3271 box. There is also a |shift_amount| field
, a scaled integer indicating
3272 how much this box should be lowered
(if it appears in a horizontal list
),
3273 or how much it should be moved to the right
(if it appears in a vertical
3274 list
). There is a |list_ptr| field
, which points to the beginning of the
3275 list from which this box was fabricated
; if |list_ptr| is |null|
, the box
3276 is empty. Finally
, there are three fields that represent the setting of
3277 the glue
: |glue_set
(p
)| is a word of type |glue_ratio| that represents
3278 the proportionality constant for glue setting
; |glue_sign
(p
)| is
3279 |stretching| or |shrinking| or |normal| depending on whether or not the
3280 glue should stretch or shrink or remain rigid
; and |glue_order
(p
)|
3281 specifies the order of infinity to which glue setting applies
(|normal|
,
3282 |sfi|
, |fil|
, |fill|
, or |filll|
). The |subtype| field is not used.
3284 @ The |new_null_box| function returns a pointer to an |hlist_node| in
3285 which all subfields have the values corresponding to `\.
{\\hbox\
{\
}}'.
3286 The |subtype| field is set to |min_quarterword|
, since that's the desired
3287 |span_count| value if this |hlist_node| is changed to an |unset_node|.
3290 halfword new_null_box
(void
)
3291 { /* creates a new box node
*/
3292 halfword p
= new_node
(hlist_node
, min_quarterword
);
3293 box_dir
(p
) = text_direction
;
3297 @ A |vlist_node| is like an |hlist_node| in all respects except that it
3298 contains a vertical list.
3300 @ A |rule_node| stands for a solid black rectangle
; it has |width|
,
3301 |depth|
, and |height| fields just as in an |hlist_node|. However
, if
3302 any of these dimensions is $
-2^
{30}$
, the actual value will be determined
3303 by running the rule up to the boundary of the innermost enclosing box.
3304 This is called a ``running dimension.'' The |width| is never running in
3305 an hlist
; the |height| and |depth| are never running in a~vlist.
3307 @ A new rule node is delivered by the |new_rule| function. It
3308 makes all the dimensions ``running
,'' so you have to change the
3309 ones that are not allowed to run.
3312 halfword new_rule
(int s
)
3314 halfword p
= new_node
(rule_node
,s
);
3318 @ Insertions are represented by |ins_node| records
, where the |subtype|
3319 indicates the corresponding box number. For example
, `\.
{\\insert
250}'
3320 leads to an |ins_node| whose |subtype| is |
250+min_quarterword|.
3321 The |height| field of an |ins_node| is slightly misnamed
; it actually holds
3322 the natural height plus depth of the vertical list being inserted.
3323 The |depth| field holds the |split_max_depth| to be used in case this
3324 insertion is split
, and the |split_top_ptr| points to the corresponding
3325 |split_top_skip|. The |float_cost| field holds the |floating_penalty| that
3326 will be used if this insertion floats to a subsequent page after a
3327 split insertion of the same class. There is one more field
, the
3328 |ins_ptr|
, which points to the beginning of the vlist for the insertion.
3330 @ A |mark_node| has a |mark_ptr| field that points to the reference count
3331 of a token list that contains the user's \.
{\\mark
} text.
3332 In addition there is a |mark_class| field that contains the mark class.
3334 @ An |adjust_node|
, which occurs only in horizontal lists
,
3335 specifies material that will be moved out into the surrounding
3336 vertical list
; i.e.
, it is used to implement \TeX's `\.
{\\vadjust
}'
3337 operation. The |adjust_ptr| field points to the vlist containing this
3340 @ A |glyph_node|
, which occurs only in horizontal lists
, specifies a
3341 glyph in a particular font
, along with its attribute list. Older
3342 versions of \TeX\ could use token memory for characters
, because the
3343 font
,char combination would fit in a single word
(both values were
3344 required to be strictly less than $
2^
{16}$
). In LuaTeX
, room is
3345 needed for characters that are larger than that
, as well as a pointer
3346 to a potential attribute list
, and the two displacement values.
3348 In turn
, that made the node so large that it made sense to merge
3349 ligature glyphs as well
, as that requires only one extra pointer. A
3350 few extra classes of glyph nodes will be introduced later. The
3351 unification of all those types makes it easier to manipulate lists of
3352 glyphs. The subtype differentiates various glyph kinds.
3354 First
, here is a function that returns a pointer to a glyph node for a given
3355 glyph in a given font. If that glyph doesn't exist
, |null| is returned
3356 instead. Nodes of this subtype are directly created only for accents
3357 and their base
(through |make_accent|
), and math nucleus items
(in the
3358 conversion from |mlist| to |hlist|
).
3361 halfword new_glyph
(int f
, int c
)
3363 halfword p
= null
; /* the new node
*/
3364 if
((f
== 0) ||
(char_exists
(f
, c
))) {
3365 p
= new_glyph_node
();
3373 @ A subset of the glyphs nodes represent ligatures
: characters
3374 fabricated from the interaction of two or more actual characters. The
3375 characters that generated the ligature have not been forgotten
, since
3376 they are needed for diagnostic messages
; the |lig_ptr| field points to
3377 a linked list of character nodes for all original characters that have
3378 been deleted.
(This list might be empty if the characters that
3379 generated the ligature were retained in other nodes.
)
3381 The |subtype| field of these |glyph_node|s is
1, plus
2 and
/or
1 if
3382 the original source of the ligature included implicit left and
/or
3383 right boundaries. These nodes are created by the C function |new_ligkern|.
3385 A third general type of glyphs could be called a character
, as it
3386 only appears in lists that are not yet processed by the ligaturing and
3387 kerning steps of the program.
3389 |main_control| inserts these
, and they are later converted to
3390 |subtype_normal| by |new_ligkern|.
3393 quarterword norm_min
(int h
)
3400 return
(quarterword
) h
;
3403 halfword new_char
(int f
, int c
)
3405 halfword p
; /* the new node
*/
3406 p
= new_glyph_node
();
3407 set_to_character
(p
);
3410 lang_data
(p
) = make_lang_data
(uc_hyph
, cur_lang
, left_hyphen_min
, right_hyphen_min
);
3414 @ Left and right ghost glyph nodes are the result of \.
{\\leftghost
}
3415 and \.
{\\rightghost
}, respectively. They are going to be removed by
3416 |new_ligkern|
, at the end of which they are no longer needed.
3418 @ Here are a few handy helpers used by the list output routines.
3421 scaled glyph_width
(halfword p
)
3423 scaled w
= char_width
(font
(p
), character
(p
));
3427 scaled glyph_height
(halfword p
)
3429 scaled w
= char_height
(font
(p
), character
(p
)) + y_displace
(p
);
3435 scaled glyph_depth
(halfword p
)
3437 scaled w
= char_depth
(font
(p
), character
(p
));
3438 if
(y_displace
(p
) > 0)
3439 w
= w
- y_displace
(p
);
3445 @ A |disc_node|
, which occurs only in horizontal lists
, specifies a
3446 ``dis\
-cretion\
-ary'' line break. If such a break occurs at node |p|
, the text
3447 that starts at |pre_break
(p
)| will precede the break
, the text that starts at
3448 |post_break
(p
)| will follow the break
, and text that appears in
3449 |no_break
(p
)| nodes will be ignored. For example
, an ordinary
3450 discretionary hyphen
, indicated by `\.
{\\
-}'
, yields a |disc_node| with
3451 |pre_break| pointing to a |char_node| containing a hyphen
, |post_break
=null|
,
3452 and |no_break
=null|.
3454 {TODO
: Knuth said
: All three of the discretionary texts must be lists
3455 that consist entirely of character
, kern
, box and rule nodes.
}
3457 If |subtype
(p
)=automatic_disc|
, the |ex_hyphen_penalty| will be charged for this
3458 break. Otherwise the |hyphen_penalty| will be charged. The texts will
3459 actually be substituted into the list by the line-breaking algorithm if it
3460 decides to make the break
, and the discretionary node will disappear at
3461 that time
; thus
, the output routine sees only discretionaries that were
3465 halfword new_disc
(void
)
3466 { /* creates an empty |disc_node|
*/
3467 halfword p
= new_node
(disc_node
, 0);
3468 disc_penalty
(p
) = int_par
(hyphen_penalty_code
);
3472 @ A |whatsit_node| is a wild card reserved for extensions to \TeX. The
3473 |subtype| field in its first word says what `\\
{whatsit
}' it is
, and
3474 implicitly determines the node size
(which must be
2 or more
) and the
3475 format of the remaining words. When a |whatsit_node| is encountered
3476 in a list
, special actions are invoked
; knowledgeable people who are
3477 careful not to mess up the rest of \TeX\ are able to make \TeX\ do new
3478 things by adding code at the end of the program. For example
, there
3479 might be a `\TeX nicolor' extension to specify different colors of ink
,
3480 @^extensions to \TeX@
>
3481 and the whatsit node might contain the desired parameters.
3483 The present implementation of \TeX\ treats the features associated with
3484 `\.
{\\write
}' and `\.
{\\special
}' as if they were extensions
, in order to
3485 illustrate how such routines might be coded. We shall defer further
3486 discussion of extensions until the end of this program.
3488 @ A |math_node|
, which occurs only in horizontal lists
, appears before and
3489 after mathematical formulas. The |subtype| field is |before| before the
3490 formula and |after| after it. There is a |surround| field
, which represents
3491 the amount of surrounding space inserted by \.
{\\mathsurround
}.
3494 halfword new_math
(scaled w
, int s
)
3496 halfword p
= new_node
(math_node
, s
);
3501 @ \TeX\ makes use of the fact that |hlist_node|
, |vlist_node|
,
3502 |rule_node|
, |ins_node|
, |mark_node|
, |adjust_node|
,
3503 |disc_node|
, |whatsit_node|
, and |math_node| are at the low end of the
3504 type codes
, by permitting a break at glue in a list if and only if the
3505 |type| of the previous node is less than |math_node|. Furthermore
, a
3506 node is discarded after a break if its type is |math_node| or~more.
3508 @ A |glue_node| represents glue in a list. However
, it is really only
3509 a pointer to a separate glue specification
, since \TeX\ makes use of the
3510 fact that many essentially identical nodes of glue are usually present.
3511 If |p| points to a |glue_node|
, |glue_ptr
(p
)| points to
3512 another packet of words that specify the stretch and shrink components
, etc.
3514 Glue nodes also serve to represent leaders
; the |subtype| is used to
3515 distinguish between ordinary glue
(which is called |normal|
) and the three
3516 kinds of leaders
(which are called |a_leaders|
, |c_leaders|
, and |x_leaders|
).
3517 The |leader_ptr| field points to a rule node or to a box node containing the
3518 leaders
; it is set to |null| in ordinary glue nodes.
3520 Many kinds of glue are computed from \TeX's ``skip'' parameters
, and
3521 it is helpful to know which parameter has led to a particular glue node.
3522 Therefore the |subtype| is set to indicate the source of glue
, whenever
3523 it originated as a parameter. We will be defining symbolic names for the
3524 parameter numbers later
(e.g.
, |line_skip_code
=0|
, |baseline_skip_code
=1|
,
3525 etc.
); it suffices for now to say that the |subtype| of parametric glue
3526 will be the same as the parameter number
, plus~one.
3528 @ In math formulas there are two more possibilities for the |subtype| in a
3529 glue node
: |mu_glue| denotes an \.
{\\mskip
} (where the units are scaled \.
{mu
}
3530 instead of scaled \.
{pt
}); and |cond_math_glue| denotes the `\.
{\\nonscript
}'
3531 feature that cancels the glue node immediately following if it appears
3534 @ A glue specification has a halfword reference count in its first word
,
3535 @^reference counts@
>
3536 representing |null| plus the number of glue nodes that point to it
(less one
).
3537 Note that the reference count appears in the same position as
3538 the |link| field in list nodes
; this is the field that is initialized
3539 to |null| when a node is allocated
, and it is also the field that is flagged
3540 by |empty_flag| in empty nodes.
3542 Glue specifications also contain three |scaled| fields
, for the |width|
,
3543 |stretch|
, and |shrink| dimensions. Finally
, there are two one-byte
3544 fields called |stretch_order| and |shrink_order|
; these contain the
3545 orders of infinity
(|normal|
, |sfi|
, |fil|
, |fill|
, or |filll|
)
3546 corresponding to the stretch and shrink values.
3548 @ Here is a function that returns a pointer to a copy of a glue spec.
3549 The reference count in the copy is |null|
, because there is assumed
3550 to be exactly one reference to the new specification.
3553 halfword new_spec
(halfword p
)
3555 return copy_node
(p
== null ? zero_glue
: p
);
3558 @ And here's a function that creates a glue node for a given parameter
3559 identified by its code number
; for example
,
3560 |new_param_glue
(line_skip_code
)| returns a pointer to a glue node for the
3561 current \.
{\\lineskip
}.
3564 halfword new_param_glue
(int n
)
3566 halfword p
= new_node
(glue_node
, n
+ 1);
3567 halfword q
= glue_par
(n
);
3568 width
(p
) = width
(q
);
3569 stretch
(p
) = stretch
(q
);
3570 shrink
(p
) = shrink
(q
);
3571 stretch_order
(p
) = stretch_order
(q
);
3572 shrink_order
(p
) = shrink_order
(q
);
3576 @ Glue nodes that are more or less anonymous are created by |new_glue|
,
3577 whose argument points to a glue specification.
3580 halfword new_glue
(halfword q
)
3582 halfword p
= new_node
(glue_node
, normal
);
3583 width
(p
) = width
(q
);
3584 stretch
(p
) = stretch
(q
);
3585 shrink
(p
) = shrink
(q
);
3586 stretch_order
(p
) = stretch_order
(q
);
3587 shrink_order
(p
) = shrink_order
(q
);
3591 @ Still another subroutine is needed
: This one is sort of a combination
3592 of |new_param_glue| and |new_glue|. It creates a glue node for one of
3593 the current glue parameters
, but it makes a fresh copy of the glue
3594 specification
, since that specification will probably be subject to change
,
3595 while the parameter will stay put.
3598 The global variable |temp_ptr| is set to the address of the new spec.
3602 halfword new_skip_param
(int n
)
3604 halfword p
= new_node
(glue_node
, n
+ 1);
3605 halfword q
= glue_par
(n
);
3606 width
(p
) = width
(q
);
3607 stretch
(p
) = stretch
(q
);
3608 shrink
(p
) = shrink
(q
);
3609 stretch_order
(p
) = stretch_order
(q
);
3610 shrink_order
(p
) = shrink_order
(q
);
3614 @ A |kern_node| has a |width| field to specify a
(normally negative
)
3615 amount of spacing. This spacing correction appears in horizontal lists
3616 between letters like A and V when the font designer said that it looks
3617 better to move them closer together or further apart. A kern node can
3618 also appear in a vertical list
, when its `|width|' denotes additional
3619 spacing in the vertical direction. The |subtype| is either |normal|
(for
3620 kerns inserted from font information or math mode calculations
) or |explicit|
3621 (for kerns inserted from \.
{\\kern
} and \.
{\\
/} commands
) or |acc_kern|
3622 (for kerns inserted from non-math accents
) or |mu_glue|
(for kerns
3623 inserted from \.
{\\mkern
} specifications in math formulas
).
3625 @ The |new_kern| function creates a kern node having a given width.
3628 halfword new_kern
(scaled w
)
3630 halfword p
= new_node
(kern_node
, normal
);
3635 @ A |penalty_node| specifies the penalty associated with line or page
3636 breaking
, in its |penalty| field. This field is a fullword integer
, but
3637 the full range of integer values is not used
: Any penalty |
>=10000| is
3638 treated as infinity
, and no break will be allowed for such high values.
3639 Similarly
, any penalty |
<=-10000| is treated as negative infinity
, and a
3640 break will be forced.
3642 @ Anyone who has been reading the last few sections of the program will
3643 be able to guess what comes next.
3646 halfword new_penalty
(int m
)
3648 halfword p
= new_node
(penalty_node
, 0); /* the |subtype| is not used
*/
3653 @ You might think that we have introduced enough node types by now. Well
,
3654 almost
, but there is one more
: An |unset_node| has nearly the same format
3655 as an |hlist_node| or |vlist_node|
; it is used for entries in \.
{\\halign
}
3656 or \.
{\\valign
} that are not yet in their final form
, since the box
3657 dimensions are their ``natural'' sizes before any glue adjustment has been
3658 made. The |glue_set| word is not present
; instead
, we have a |glue_stretch|
3659 field
, which contains the total stretch of order |glue_order| that is
3660 present in the hlist or vlist being boxed.
3661 Similarly
, the |shift_amount| field is replaced by a |glue_shrink| field
,
3662 containing the total shrink of order |glue_sign| that is present.
3663 The |subtype| field is called |span_count|
; an unset box typically
3664 contains the data for |qo
(span_count
)+1| columns.
3665 Unset nodes will be changed to box nodes when alignment is completed.
3667 In fact
, there are still more types coming. When we get to math formula
3668 processing we will see that a |style_node| has |type
=14|
; and a number
3669 of larger type codes will also be defined
, for use in math mode only.
3671 Warning
: If any changes are made to these data structure layouts
, such as
3672 changing any of the node sizes or even reordering the words of nodes
,
3673 the |copy_node_list| procedure and the memory initialization code
3674 below may have to be changed. Such potentially dangerous parts of the
3675 program are listed in the index under `data structure assumptions'.
3676 @
!@^data structure assumptions@
>
3677 However
, other references to the nodes are made symbolically in terms of
3678 the \.
{WEB
} macro definitions above
, so that format changes will leave
3679 \TeX's other algorithms intact.
3680 @^system dependencies@
>
3682 @ This function creates a |local_paragraph| node
3686 halfword make_local_par_node
(int mode
)
3690 halfword p
= new_node
(local_par_node
,0);
3691 local_pen_inter
(p
) = local_inter_line_penalty
;
3692 local_pen_broken
(p
) = local_broken_penalty
;
3693 if
(local_left_box
!= null
) {
3694 q
= copy_node_list
(local_left_box
);
3695 local_box_left
(p
) = q
;
3696 local_box_left_width
(p
) = width
(local_left_box
);
3698 if
(local_right_box
!= null
) {
3699 q
= copy_node_list
(local_right_box
);
3700 local_box_right
(p
) = q
;
3701 local_box_right_width
(p
) = width
(local_right_box
);
3703 local_par_dir
(p
) = par_direction
;
3704 /* callback with node passed
*/
3705 callback_id
= callback_defined
(insert_local_par_callback
);
3706 if
(callback_id
> 0) {
3707 int sfix
= lua_gettop
(Luas
);
3708 if
(!get_callback
(Luas
, callback_id
)) {
3709 lua_settop
(Luas
, sfix
);
3712 nodelist_to_lua
(Luas
, p
);
3713 lua_push_local_par_mode
(Luas
,mode
)
3714 if
(lua_pcall
(Luas
, 2, 0, 0) != 0) { /* 2 arg
, 0 result
*/
3715 char errmsg
[256]; /* temp hack ... we will have a formatted error
*/
3716 snprintf
(errmsg
, 255, "error: %s\n", lua_tostring
(Luas
, -1));
3718 lua_settop
(Luas
, sfix
);
3719 normal_error
("insert_local_par",errmsg
); /* to be done
*/
3722 lua_settop
(Luas
, sfix
);