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_penalty
[] = {
284 const char
*node_subtypes_kern
[] = {
285 "fontkern", "userkern", "accentkern", "italiccorrection", NULL
287 const char
*node_subtypes_rule
[] = {
288 "normal", "box", "image", "empty", "user", NULL
290 const char
*node_subtypes_glyph
[] = {
291 "character", "glyph", "ligature", "ghost", "left", "right", NULL
293 const char
*node_subtypes_disc
[] = {
294 "discretionary", "explicit", "automatic", "regular", "first", "second", NULL
296 const char
*node_subtypes_marginkern
[] = {
297 "left", "right", NULL
299 const char
*node_subtypes_list
[] = {
300 "unknown", "line", "box", "indent", "alignment", "cell", "equation", "equationnumber", NULL
302 const char
*node_subtypes_adjust
[] = {
303 "normal", "pre", NULL
305 const char
*node_subtypes_math
[] = {
306 "beginmath", "endmath", NULL
308 const char
*node_subtypes_noad
[] = {
309 "ord", "opdisplaylimits", "oplimits", "opnolimits", "bin", "rel", "open", "close",
310 "punct", "inner", "under", "over", "vcenter", NULL
312 const char
*node_subtypes_radical
[] = {
313 "radical", "uradical", "uroot", "uunderdelimiter", "uoverdelimiter", "udelimiterunder",
314 "udelimiterover", NULL
316 const char
*node_subtypes_accent
[] = {
317 "bothflexible", "fixedtop", "fixedbottom", "fixedboth", NULL,
319 const char
*node_subtypes_fence
[] = {
320 "unset", "left", "middle", "right", NULL
323 node_info node_data
[] = { /* the last entry in a row is the etex number
*/
324 { hlist_node
, box_node_size
, node_fields_list
, "hlist", 1 },
325 { vlist_node
, box_node_size
, node_fields_list
, "vlist", 2 },
326 { rule_node
, rule_node_size
, node_fields_rule
, "rule", 3 },
327 { ins_node
, ins_node_size
, node_fields_insert
, "ins", 4 },
328 { mark_node
, mark_node_size
, node_fields_mark
, "mark", 5 },
329 { adjust_node
, adjust_node_size
, node_fields_adjust
, "adjust", 6 },
330 { boundary_node
, boundary_size
, node_fields_boundary
, "boundary", -1 },
331 { disc_node
, disc_node_size
, node_fields_disc
, "disc", 8 },
332 { whatsit_node
, -1, NULL, "whatsit", 9 },
333 { local_par_node
, local_par_size
, node_fields_local_par
, "local_par", -1 },
334 { dir_node
, dir_node_size
, node_fields_dir
, "dir", -1 },
335 { math_node
, math_node_size
, node_fields_math
, "math", 10 },
336 { glue_node
, glue_node_size
, node_fields_glue
, "glue", 11 },
337 { kern_node
, kern_node_size
, node_fields_kern
, "kern", 12 },
338 { penalty_node
, penalty_node_size
, node_fields_penalty
, "penalty", 13 },
339 { unset_node
, box_node_size
, node_fields_unset
, "unset", 14 },
340 { style_node
, style_node_size
, node_fields_style
, "style", 15 },
341 { choice_node
, style_node_size
, node_fields_choice
, "choice", 15 },
342 { simple_noad
, noad_size
, node_fields_ord
, "noad", 15 },
343 { radical_noad
, radical_noad_size
, node_fields_radical
, "radical", 15 },
344 { fraction_noad
, fraction_noad_size
, node_fields_fraction
, "fraction", 15 },
345 { accent_noad
, accent_noad_size
, node_fields_accent
, "accent", 15 },
346 { fence_noad
, fence_noad_size
, node_fields_fence
, "fence", 15 },
347 { math_char_node
, math_kernel_node_size
, node_fields_math_char
, "math_char", 15 },
348 { sub_box_node
, math_kernel_node_size
, node_fields_sub_box
, "sub_box", 15 },
349 { sub_mlist_node
, math_kernel_node_size
, node_fields_sub_mlist
, "sub_mlist", 15 },
350 { math_text_char_node
, math_kernel_node_size
, node_fields_math_text_char
, "math_text_char", 15 },
351 { delim_node
, math_shield_node_size
, node_fields_delim
, "delim", 15 },
352 { margin_kern_node
, margin_kern_node_size
, node_fields_margin_kern
, "margin_kern", -1 },
353 { glyph_node
, glyph_node_size
, node_fields_glyph
, "glyph", 0 },
354 { align_record_node
, box_node_size
, NULL, "align_record", -1 },
355 { pseudo_file_node
, pseudo_file_node_size
, NULL, "pseudo_file", -1 },
356 { pseudo_line_node
, variable_node_size
, NULL, "pseudo_line", -1 },
357 { inserting_node
, page_ins_node_size
, node_fields_inserting
, "page_insert", -1 },
358 { split_up_node
, page_ins_node_size
, node_fields_splitup
, "split_insert", -1 },
359 { expr_node
, expr_node_size
, NULL, "expr_stack", -1 },
360 { nesting_node
, nesting_node_size
, NULL, "nested_list", -1 },
361 { span_node
, span_node_size
, NULL, "span", -1 },
362 { attribute_node
, attribute_node_size
, node_fields_attribute
, "attribute", -1 },
363 { glue_spec_node
, glue_spec_size
, node_fields_glue_spec
, "glue_spec", -1 },
364 { attribute_list_node
, attribute_node_size
, node_fields_attribute_list
, "attribute_list", -1 },
365 { temp_node
, temp_node_size
, NULL, "temp", -1 },
366 { align_stack_node
, align_stack_node_size
, NULL, "align_stack", -1 },
367 { movement_node
, movement_node_size
, NULL, "movement_stack", -1 },
368 { if_node
, if_node_size
, NULL, "if_stack", -1 },
369 { unhyphenated_node
, active_node_size
, NULL, "unhyphenated", -1 },
370 { hyphenated_node
, active_node_size
, NULL, "hyphenated", -1 },
371 { delta_node
, delta_node_size
, NULL, "delta", -1 },
372 { passive_node
, passive_node_size
, NULL, "passive", -1 },
373 { shape_node
, variable_node_size
, NULL, "shape", -1 },
374 { -1, -1, NULL, NULL, -1 },
377 #define last_normal_node shape_node
379 const char
*node_subtypes_pdf_destination
[] = {
380 "xyz", "fit", "fith", "fitv", "fitb", "fitbh", "fitbv", "fitr", NULL
382 const char
*node_subtypes_pdf_literal
[] = {
383 "origin", "page", "direct", NULL
386 node_info whatsit_node_data
[] = {
387 { open_node
, open_node_size
, node_fields_whatsit_open
, "open", -1 },
388 { write_node
, write_node_size
, node_fields_whatsit_write
, "write", -1 },
389 { close_node
, close_node_size
, node_fields_whatsit_close
, "close", -1 },
390 { special_node
, special_node_size
, node_fields_whatsit_special
, "special", -1 },
391 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
392 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
393 { save_pos_node
, save_pos_node_size
, node_fields_whatsit_save_pos
, "save_pos", -1 },
394 { late_lua_node
, late_lua_node_size
, node_fields_whatsit_late_lua
, "late_lua", -1 },
395 { user_defined_node
, user_defined_node_size
, node_fields_whatsit_user_defined
, "user_defined", -1 },
396 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
397 { fake_node
, fake_node_size
, NULL, fake_node_name
, -1 },
398 { fake_node
, fake_node_size
, NULL, fake_node_name
, -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 /* here starts the dvi backend section
, todo
: a separate list
*/
404 /* nothing for dvi
*/
405 /* here starts the pdf backend section
, todo
: a separate list
*/
406 { pdf_literal_node
, write_node_size
, node_fields_whatsit_pdf_literal
, "pdf_literal", -1 },
407 { pdf_refobj_node
, pdf_refobj_node_size
, node_fields_whatsit_pdf_refobj
, "pdf_refobj", -1 },
408 { pdf_annot_node
, pdf_annot_node_size
, node_fields_whatsit_pdf_annot
, "pdf_annot", -1 },
409 { pdf_start_link_node
, pdf_annot_node_size
, node_fields_whatsit_pdf_start_link
, "pdf_start_link", -1 },
410 { pdf_end_link_node
, pdf_end_link_node_size
, node_fields_whatsit_pdf_end_link
, "pdf_end_link", -1 },
411 { pdf_dest_node
, pdf_dest_node_size
, node_fields_whatsit_pdf_dest
, "pdf_dest", -1 },
412 { pdf_action_node
, pdf_action_size
, node_fields_whatsit_pdf_action
, "pdf_action", -1 },
413 { pdf_thread_node
, pdf_thread_node_size
, node_fields_whatsit_pdf_thread
, "pdf_thread", -1 },
414 { pdf_start_thread_node
, pdf_thread_node_size
, node_fields_whatsit_pdf_start_thread
, "pdf_start_thread", -1 },
415 { pdf_end_thread_node
, pdf_end_thread_node_size
, node_fields_whatsit_pdf_end_thread
, "pdf_end_thread", -1 },
416 { pdf_thread_data_node
, pdf_thread_node_size
, NULL, "pdf_thread_data", -1 },
417 { pdf_link_data_node
, pdf_annot_node_size
, NULL, "pdf_link_data", -1 },
418 { pdf_colorstack_node
, pdf_colorstack_node_size
, node_fields_whatsit_pdf_colorstack
, "pdf_colorstack", -1 },
419 { pdf_setmatrix_node
, pdf_setmatrix_node_size
, node_fields_whatsit_pdf_setmatrix
, "pdf_setmatrix", -1 },
420 { pdf_save_node
, pdf_save_node_size
, node_fields_whatsit_pdf_save
, "pdf_save", -1 },
421 { pdf_restore_node
, pdf_restore_node_size
, node_fields_whatsit_pdf_restore
, "pdf_restore", -1 },
423 { -1, -1, NULL, NULL, -1 },
426 #define last_whatsit_node pdf_restore_node
429 When we copy a node list
, there are several possibilities
: we do the same as a new node
,
430 we copy the entry to the table in properties
(a reference
), we do a deep copy of a table
431 in the properties
, we create a new table and give it the original one as a metatable.
432 After some experiments
(that also included timing
) with these scenarios I decided that a
433 deep copy made no sense
, nor did nilling. In the end both the shallow copy and the metatable
434 variant were both ok
, although the second ons is slower. The most important aspect to keep
435 in mind is that references to other nodes in properties no longer can be valid for that
436 copy. We could use two tables
(one unique and one shared
) or metatables but that only
439 When defining a new node
, we could already allocate a table but it is rather easy to do
440 that at the lua end e.g. using a metatable __index method. That way it is under macro
443 When deleting a node
, we could keep the slot
(e.g. setting it to false
) but it could make
444 memory consumption raise unneeded when we have temporary large node lists and after that
447 So
, in the end this is what we ended up with. For the record
, I also experimented with the
450 - copy attributes to the properties so that we have fast access at the lua end
: in the end
451 the overhead is not compensated by speed and convenience
, in fact
, attributes are not
452 that slow when it comes to accessing them
454 - a bitset in the node but again the gain compared to attributes is neglectable and it also
455 demands a pretty string agreement over what bit represents what
, and this is unlikely to
456 succeed in the tex community
(I could use it for font handling
, which is cross package
,
457 but decided that it doesn't pay off
459 In case one wonders why properties make sense then
, well
, it is not so much speed that we
460 gain
, but more convenience
: storing all kind of
(temporary
) data in attributes is no fun and
461 this mechanism makes sure that properties are cleaned up when a node is freed. Also
, the
462 advantage of a more or less global properties table is that we stay at the lua end. An
463 alternative is to store a reference in the node itself but that is complicated by the fact
464 that the register has some limitations
(no numeric keys
) and we also don't want to mess with
468 int lua_properties_level
= 0 ; /* can be private
*/
469 int lua_properties_enabled
= 0 ;
470 int lua_properties_use_metatable
= 0 ;
473 We keep track of nesting so that we don't oveflow the stack
, and
, what is more
474 important
, don't keep resolving the registry index.
477 #define lua_properties_push do
{ \
478 if
(lua_properties_enabled
) { \
479 lua_properties_level
= lua_properties_level
+ 1 ; \
480 if
(lua_properties_level
== 1) { \
481 lua_get_metatablelua_l
(Luas
,node_properties
); \
486 #define lua_properties_pop do
{ \
487 if
(lua_properties_enabled
) { \
488 if
(lua_properties_level
== 1) \
490 lua_properties_level
= lua_properties_level
- 1 ; \
494 /* No setting is needed
: */
496 #define lua_properties_set
(target
) do
{ \
499 /* Resetting boils down to nilling.
*/
501 #define lua_properties_reset
(target
) do
{ \
502 if
(lua_properties_enabled
) { \
503 if
(lua_properties_level
== 0) { \
504 lua_get_metatablelua_l
(Luas
,node_properties
); \
506 lua_rawseti
(Luas
,-2,target
); \
510 lua_rawseti
(Luas
,-2,target
); \
516 For a moment I considered supporting all kind of data types but in practice
517 that makes no sense. So we stick to a cheap shallow copy with as option a
518 metatable. Btw
, a deep copy would look like this
:
520 static void copy_lua_table
(lua_State
* L
, int index
) {
523 while
(lua_next
(L
, index-1
) != 0) {
524 lua_pushvalue
(L
, -2);
526 if
(lua_type
(L
,-1)==LUA_TTABLE
)
527 copy_lua_table
(L
,-1);
533 #define lua_properties_copy
(target
, source
) do
{ \
534 if
(lua_properties_enabled
) { \
535 lua_pushinteger
(Luas
,source
); \
536 lua_rawget
(Luas
,-2); \
537 if
(lua_type
(Luas
,-1)==LUA_TTABLE
) { \
538 copy_lua_table
(Luas
,-1); \
539 lua_pushinteger
(Luas
,target
); \
540 lua_insert
(Luas
,-2); \
541 lua_rawset
(Luas
,-3); \
550 /* isn't there a faster way to metatable?
*/
552 #define lua_properties_copy
(target
,source
) do
{ \
553 if
(lua_properties_enabled
) { \
554 if
(lua_properties_level
== 0) { \
555 lua_get_metatablelua_l
(Luas
,node_properties
); \
556 lua_rawgeti
(Luas
,-1,source
); \
557 if
(lua_type
(Luas
,-1)==LUA_TTABLE
) { \
558 if
(lua_properties_use_metatable
) { \
559 lua_newtable
(Luas
); \
560 lua_insert
(Luas
,-2); \
561 lua_setfield
(Luas
,-2,"__index"); \
562 lua_newtable
(Luas
); \
563 lua_insert
(Luas
,-2); \
564 lua_setmetatable
(Luas
,-2); \
566 lua_rawseti
(Luas
,-2,target
); \
572 lua_rawgeti
(Luas
,-1,source
); \
573 if
(lua_type
(Luas
,-1)==LUA_TTABLE
) { \
574 if
(lua_properties_use_metatable
) { \
575 lua_newtable
(Luas
); \
576 lua_insert
(Luas
,-2); \
577 lua_setfield
(Luas
,-2,"__index"); \
578 lua_newtable
(Luas
); \
579 lua_insert
(Luas
,-2); \
580 lua_setmetatable
(Luas
,-2); \
582 lua_rawseti
(Luas
,-2,target
); \
590 /* Here end the property handlers.
*/
593 int valid_node
(halfword p
)
595 if
(p
> my_prealloc
&& p < var_mem_max) {
596 #ifdef CHECK_NODE_USAGE
597 if
(varmem_sizes
[p
] > 0) {
608 static int test_count
= 1;
610 #define dorangetest
(a
,b
,c
) do
{ \
611 if
(!(b
>=0 && b<c)) { \
612 fprintf
(stdout
,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \
613 (int
)a
, (int
)b
, (int
)c
, __LINE__
,test_count
); \
614 confusion
("node range test failed"); \
617 #define dotest
(a
,b
,c
) do
{ \
619 fprintf
(stdout
,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \
620 (int
)a
, (int
)b
, (int
)c
, __LINE__
,test_count
); \
621 confusion
("node test failed"); \
624 #define check_action_ref
(a
) { dorangetest
(p
,a
,var_mem_max
); }
625 #define check_attribute_ref
(a
) { dorangetest
(p
,a
,var_mem_max
); }
626 #define check_token_ref
(a
) { confusion
("fuzzy token cleanup in node"); }
628 #ifdef CHECK_NODE_USAGE
630 static void check_static_node_mem
(void
)
632 dotest
(zero_glue
, width
(zero_glue
), 0);
633 dotest
(zero_glue
, type
(zero_glue
), glue_spec_node
);
634 dotest
(zero_glue
, vlink
(zero_glue
), null
);
635 dotest
(zero_glue
, stretch
(zero_glue
), 0);
636 dotest
(zero_glue
, stretch_order
(zero_glue
), normal
);
637 dotest
(zero_glue
, shrink
(zero_glue
), 0);
638 dotest
(zero_glue
, shrink_order
(zero_glue
), normal
);
640 dotest
(sfi_glue
, width
(sfi_glue
), 0);
641 dotest
(sfi_glue
, type
(sfi_glue
), glue_spec_node
);
642 dotest
(sfi_glue
, vlink
(sfi_glue
), null
);
643 dotest
(sfi_glue
, stretch
(sfi_glue
), 0);
644 dotest
(sfi_glue
, stretch_order
(sfi_glue
), sfi
);
645 dotest
(sfi_glue
, shrink
(sfi_glue
), 0);
646 dotest
(sfi_glue
, shrink_order
(sfi_glue
), normal
);
648 dotest
(fil_glue
, width
(fil_glue
), 0);
649 dotest
(fil_glue
, type
(fil_glue
), glue_spec_node
);
650 dotest
(fil_glue
, vlink
(fil_glue
), null
);
651 dotest
(fil_glue
, stretch
(fil_glue
), unity
);
652 dotest
(fil_glue
, stretch_order
(fil_glue
), fil
);
653 dotest
(fil_glue
, shrink
(fil_glue
), 0);
654 dotest
(fil_glue
, shrink_order
(fil_glue
), normal
);
656 dotest
(fill_glue
, width
(fill_glue
), 0);
657 dotest
(fill_glue
, type
(fill_glue
), glue_spec_node
);
658 dotest
(fill_glue
, vlink
(fill_glue
), null
);
659 dotest
(fill_glue
, stretch
(fill_glue
), unity
);
660 dotest
(fill_glue
, stretch_order
(fill_glue
), fill
);
661 dotest
(fill_glue
, shrink
(fill_glue
), 0);
662 dotest
(fill_glue
, shrink_order
(fill_glue
), normal
);
664 dotest
(ss_glue
, width
(ss_glue
), 0);
665 dotest
(ss_glue
, type
(ss_glue
), glue_spec_node
);
666 dotest
(ss_glue
, vlink
(ss_glue
), null
);
667 dotest
(ss_glue
, stretch
(ss_glue
), unity
);
668 dotest
(ss_glue
, stretch_order
(ss_glue
), fil
);
669 dotest
(ss_glue
, shrink
(ss_glue
), unity
);
670 dotest
(ss_glue
, shrink_order
(ss_glue
), fil
);
672 dotest
(fil_neg_glue
, width
(fil_neg_glue
), 0);
673 dotest
(fil_neg_glue
, type
(fil_neg_glue
), glue_spec_node
);
674 dotest
(fil_neg_glue
, vlink
(fil_neg_glue
), null
);
675 dotest
(fil_neg_glue
, stretch
(fil_neg_glue
), -unity
);
676 dotest
(fil_neg_glue
, stretch_order
(fil_neg_glue
), fil
);
677 dotest
(fil_neg_glue
, shrink
(fil_neg_glue
), 0);
678 dotest
(fil_neg_glue
, shrink_order
(fil_neg_glue
), normal
);
681 static void node_mem_dump
(halfword p
)
684 for
(r
= my_prealloc
+ 1; r
< var_mem_max
; r
++) {
687 while
(s
> my_prealloc
&& varmem_sizes[s] == 0) {
693 && (r - s) < get_node_size(type(s), subtype(s))
695 if
(type
(s
) == disc_node
) {
696 fprintf
(stdout
," pointed to from %s node %d (vlink %d, alink %d): ",
697 get_node_name
(type
(s
), subtype
(s
)), (int
) s
,
698 (int
) vlink
(s
), (int
) alink
(s
));
699 fprintf
(stdout
, "pre_break(%d,%d,%d), ",
700 (int
) vlink_pre_break
(s
), (int
) tlink
(pre_break
(s
)),
701 (int
) alink
(pre_break
(s
)));
702 fprintf
(stdout
, "post_break(%d,%d,%d), ",
703 (int
) vlink_post_break
(s
),
704 (int
) tlink
(post_break
(s
)),
705 (int
) alink
(post_break
(s
)));
706 fprintf
(stdout
, "no_break(%d,%d,%d)",
707 (int
) vlink_no_break
(s
), (int
) tlink
(no_break
(s
)),
708 (int
) alink
(no_break
(s
)));
709 fprintf
(stdout
, "\n");
712 ||
(type
(s
) == glyph_node
&& lig_ptr (s) == p)
713 ||
(type
(s
) == vlist_node
&& list_ptr(s) == p)
714 ||
(type
(s
) == hlist_node
&& list_ptr(s) == p)
715 ||
(type
(s
) == unset_node
&& list_ptr(s) == p)
716 ||
(type
(s
) == ins_node
&& ins_ptr (s) == p)
718 fprintf
(stdout
," pointed to from %s node %d (vlink %d, alink %d): ",
719 get_node_name
(type
(s
), subtype
(s
)), (int
) s
,
720 (int
) vlink
(s
), (int
) alink
(s
));
721 if
(type
(s
) == glyph_node
) {
722 fprintf
(stdout
, "lig_ptr(%d)", (int
) lig_ptr
(s
));
723 } else if
(type
(s
) == vlist_node || type
(s
) == hlist_node
) {
724 fprintf
(stdout
, "list_ptr(%d)", (int
) list_ptr
(s
));
726 fprintf
(stdout
, "\n");
728 if
((type
(s
) != penalty_node
) && (type(s) != math_node) && (type(s) != kern_node)) {
729 fprintf
(stdout
, " pointed to from %s node %d\n",
730 get_node_name
(type
(s
), subtype
(s
)), (int
) s
);
741 static int free_error
(halfword p
)
743 if
(p
> my_prealloc
&& p < var_mem_max) {
744 #ifdef CHECK_NODE_USAGE
746 if
(varmem_sizes
[p
] == 0) {
747 check_static_node_mem
();
748 for
(i
= (my_prealloc
+ 1); i
< var_mem_max
; i
++) {
749 if
(varmem_sizes
[i
] > 0) {
754 if
(type
(p
) == glyph_node
) {
755 formatted_error
("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int
) character
(p
), (int
) p
);
757 formatted_error
("nodes", "attempt to double-free %s node %d, ignored", get_node_name
(type
(p
), subtype
(p
)), (int
) p
);
764 formatted_error
("nodes", "attempt to free an impossible node %d", (int
) p
);
771 static int copy_error
(halfword p
)
773 if
(p
>= 0 && p < var_mem_max) {
774 #ifdef CHECK_NODE_USAGE
775 if
(p
> my_prealloc
&& varmem_sizes[p] == 0) {
776 if
(type
(p
) == glyph_node
) {
777 formatted_warning
("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int
) character
(p
), (int
) p
);
779 formatted_warning
("nodes", "attempt to copy free %s node %d, ignored", get_node_name
(type
(p
), subtype
(p
)), (int
) p
);
785 formatted_error
("nodes", "attempt to copy an impossible node %d", (int
) p
);
792 halfword new_node
(int i
, int j
)
794 int s
= get_node_size
(i
, j
);
795 halfword n
= get_node
(s
);
797 It should be possible to do this memset at |free_node
()|.
799 Both type
() and subtype
() will be set below
, and vlink
() is
800 set to null by |get_node
()|
, so we can do we clearing one
803 (void
) memset
((void
*) (varmem
+ n
+ 1), 0, (sizeof
(memory_word
) * ((unsigned
) s
- 1)));
813 pre_break
(n
) = pre_break_head
(n
);
814 type
(pre_break
(n
)) = nesting_node
;
815 subtype
(pre_break
(n
)) = pre_break_head
(0);
816 post_break
(n
) = post_break_head
(n
);
817 type
(post_break
(n
)) = nesting_node
;
818 subtype
(post_break
(n
)) = post_break_head
(0);
819 no_break
(n
) = no_break_head
(n
);
820 type
(no_break
(n
)) = nesting_node
;
821 subtype
(no_break
(n
)) = no_break_head
(0);
824 depth
(n
) = null_flag
;
825 height
(n
) = null_flag
;
826 width
(n
) = null_flag
;
829 rule_transform
(n
) = 0;
832 if
(j
== open_node
) {
833 open_name
(n
) = get_nullstr
();
834 open_area
(n
) = open_name
(n
);
835 open_ext
(n
) = open_name
(n
);
839 width
(n
) = null_flag
;
841 case pseudo_line_node
:
843 /* this is a trick that makes |pseudo_files| slightly slower
,
844 but the overall allocation faster then an explicit test
845 at the top of |new_node
()|.
848 free_node
(n
, variable_node_size
);
849 n
= slow_get_node
(j
);
850 (void
) memset
((void
*) (varmem
+ n
+ 1), 0, (sizeof
(memory_word
) * ((unsigned
) j
- 1)));
856 if
(int_par
(synctex_code
)) {
857 /* handle synctex extension
*/
860 synctex_tag_math
(n
) = cur_input.synctex_tag_field
;
861 synctex_line_math
(n
) = line
;
864 synctex_tag_glue
(n
) = cur_input.synctex_tag_field
;
865 synctex_line_glue
(n
) = line
;
869 synctex_tag_kern
(n
) = cur_input.synctex_tag_field
;
870 synctex_line_kern
(n
) = line
;
876 synctex_tag_box
(n
) = cur_input.synctex_tag_field
;
877 synctex_line_box
(n
) = line
;
880 synctex_tag_rule
(n
) = cur_input.synctex_tag_field
;
881 synctex_line_rule
(n
) = line
;
885 /* take care of attributes
*/
886 if
(nodetype_has_attributes
(i
)) {
887 build_attribute_list
(n
);
888 /* lua_properties_set
*/
890 type
(n
) = (quarterword
) i
;
891 subtype
(n
) = (quarterword
) j
;
895 halfword raw_glyph_node
(void
)
897 register halfword n
= get_node
(glyph_node_size
);
898 (void
) memset
((void
*) (varmem
+ n
+ 1), 0, (sizeof
(memory_word
) * (glyph_node_size
- 1)));
899 type
(n
) = glyph_node
;
904 halfword new_glyph_node
(void
)
906 register halfword n
= get_node
(glyph_node_size
);
907 (void
) memset
((void
*) (varmem
+ n
+ 1), 0, (sizeof
(memory_word
) * (glyph_node_size
- 1)));
908 type
(n
) = glyph_node
;
910 build_attribute_list
(n
);
911 /* lua_properties_set
*/
915 @ makes a duplicate of the node list that starts at |p| and returns a
916 pointer to the new list
919 halfword do_copy_node_list
(halfword p
, halfword end
)
921 halfword q
= null
; /* previous position in new list
*/
922 halfword h
= null
; /* head of the list
*/
923 register halfword s
;
924 lua_properties_push
; /* saves stack and time
*/
935 lua_properties_pop
; /* saves stack and time
*/
939 halfword copy_node_list
(halfword p
)
941 return do_copy_node_list
(p
, null
);
944 #define copy_sub_list
(target
,source
) do
{ \
945 if
(source
!= null
) { \
946 s
= do_copy_node_list
(source
, null
); \
953 #define copy_sub_node
(target
,source
) do
{ \
954 if
(source
!= null
) { \
955 s
= copy_node
(source
); \
962 @ make a dupe of a single node
965 static void copy_node_wrapup_core
(halfword p
, halfword r
)
968 switch
(subtype
(p
)) {
971 add_token_ref
(write_tokens
(p
));
976 case user_defined_node
:
977 switch
(user_node_type
(p
)) {
979 add_node_attr_ref
(user_node_value
(p
));
985 s
= copy_node_list
(user_node_value
(p
));
986 user_node_value
(r
) = s
;
989 /* |add_string_ref
(user_node_value
(p
));|
*/
992 add_token_ref
(user_node_value
(p
));
1001 void copy_node_wrapup_dvi
(halfword p
, halfword r
)
1005 void copy_node_wrapup_pdf
(halfword p
, halfword r
)
1007 switch
(subtype
(p
)) {
1008 case pdf_literal_node
:
1009 copy_pdf_literal
(r
, p
);
1011 case pdf_colorstack_node
:
1012 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
1013 add_token_ref
(pdf_colorstack_data
(p
));
1015 case pdf_setmatrix_node
:
1016 add_token_ref
(pdf_setmatrix_data
(p
));
1018 case pdf_annot_node
:
1019 add_token_ref
(pdf_annot_data
(p
));
1021 case pdf_start_link_node
:
1022 if
(pdf_link_attr
(r
) != null
)
1023 add_token_ref
(pdf_link_attr
(r
));
1024 add_action_ref
(pdf_link_action
(r
));
1027 if
(pdf_dest_named_id
(p
) > 0)
1028 add_token_ref
(pdf_dest_id
(p
));
1030 case pdf_thread_node
:
1031 case pdf_start_thread_node
:
1032 if
(pdf_thread_named_id
(p
) > 0)
1033 add_token_ref
(pdf_thread_id
(p
));
1034 if
(pdf_thread_attr
(p
) != null
)
1035 add_token_ref
(pdf_thread_attr
(p
));
1042 halfword copy_node
(const halfword p
)
1044 halfword r
; /* current node being fabricated for new list
*/
1045 halfword w
; /* whatsit subtype
*/
1046 register halfword s
; /* a helper variable for copying into variable mem
*/
1048 if
(copy_error
(p
)) {
1049 r
= new_node
(temp_node
, 0);
1052 i
= get_node_size
(type
(p
), subtype
(p
));
1055 (void
) memcpy
((void
*) (varmem
+ r
), (void
*) (varmem
+ p
), (sizeof
(memory_word
) * (unsigned
) i
));
1057 if
(int_par
(synctex_code
)) {
1058 /* handle synctex extension
*/
1061 synctex_tag_math
(r
) = cur_input.synctex_tag_field
;
1062 synctex_line_math
(r
) = line
;
1065 synctex_tag_kern
(r
) = cur_input.synctex_tag_field
;
1066 synctex_line_kern
(r
) = line
;
1070 if
(nodetype_has_attributes
(type
(p
))) {
1071 add_node_attr_ref
(node_attr
(p
));
1073 lua_properties_copy
(r
,p
);
1079 copy_sub_list
(lig_ptr
(r
),lig_ptr
(p
)) ;
1082 copy_sub_list
(leader_ptr
(r
),leader_ptr
(p
)) ;
1087 copy_sub_list
(list_ptr
(r
),list_ptr
(p
)) ;
1090 pre_break
(r
) = pre_break_head
(r
);
1091 if
(vlink_pre_break
(p
) != null
) {
1092 s
= copy_node_list
(vlink_pre_break
(p
));
1093 alink
(s
) = pre_break
(r
);
1094 tlink_pre_break
(r
) = tail_of_list
(s
);
1095 vlink_pre_break
(r
) = s
;
1097 assert
(tlink
(pre_break
(r
)) == null
);
1099 post_break
(r
) = post_break_head
(r
);
1100 if
(vlink_post_break
(p
) != null
) {
1101 s
= copy_node_list
(vlink_post_break
(p
));
1102 alink
(s
) = post_break
(r
);
1103 tlink_post_break
(r
) = tail_of_list
(s
);
1104 vlink_post_break
(r
) = s
;
1106 assert
(tlink_post_break
(r
) == null
);
1108 no_break
(r
) = no_break_head
(r
);
1109 if
(vlink
(no_break
(p
)) != null
) {
1110 s
= copy_node_list
(vlink_no_break
(p
));
1111 alink
(s
) = no_break
(r
);
1112 tlink_no_break
(r
) = tail_of_list
(s
);
1113 vlink_no_break
(r
) = s
;
1115 assert
(tlink_no_break
(r
) == null
);
1121 copy_sub_list
(ins_ptr
(r
),ins_ptr
(p
)) ;
1123 case margin_kern_node
:
1124 copy_sub_node
(margin_char
(r
),margin_char
(p
));
1127 add_token_ref
(mark_ptr
(p
));
1130 copy_sub_list
(adjust_ptr
(r
),adjust_ptr
(p
));
1133 copy_sub_list
(display_mlist
(r
),display_mlist
(p
)) ;
1134 copy_sub_list
(text_mlist
(r
),text_mlist
(p
)) ;
1135 copy_sub_list
(script_mlist
(r
),script_mlist
(p
)) ;
1136 copy_sub_list
(script_script_mlist
(r
),script_script_mlist
(p
)) ;
1139 copy_sub_list
(nucleus
(r
),nucleus
(p
)) ;
1140 copy_sub_list
(subscr
(r
),subscr
(p
)) ;
1141 copy_sub_list
(supscr
(r
),supscr
(p
)) ;
1144 copy_sub_list
(nucleus
(r
),nucleus
(p
)) ;
1145 copy_sub_list
(subscr
(r
),subscr
(p
)) ;
1146 copy_sub_list
(supscr
(r
),supscr
(p
)) ;
1147 copy_sub_node
(left_delimiter
(r
),left_delimiter
(p
)) ;
1148 copy_sub_list
(degree
(r
),degree
(p
)) ;
1151 copy_sub_list
(nucleus
(r
),nucleus
(p
)) ;
1152 copy_sub_list
(subscr
(r
),subscr
(p
)) ;
1153 copy_sub_list
(supscr
(r
),supscr
(p
)) ;
1154 copy_sub_list
(top_accent_chr
(r
),top_accent_chr
(p
)) ;
1155 copy_sub_list
(bot_accent_chr
(r
),bot_accent_chr
(p
)) ;
1156 copy_sub_list
(overlay_accent_chr
(r
),overlay_accent_chr
(p
)) ;
1159 copy_sub_node
(delimiter
(r
),delimiter
(p
)) ;
1162 case sub_mlist_node
:
1163 copy_sub_list
(math_list
(r
),math_list
(p
)) ;
1166 copy_sub_list
(numerator
(r
),numerator
(p
)) ;
1167 copy_sub_list
(denominator
(r
),denominator
(p
)) ;
1168 copy_sub_node
(left_delimiter
(r
),left_delimiter
(p
)) ;
1169 copy_sub_node
(right_delimiter
(r
),right_delimiter
(p
)) ;
1171 case glue_spec_node
:
1173 case local_par_node
:
1178 if
(w
>= backend_first_pdf_whatsit
) {
1179 copy_node_wrapup_pdf
(p
,r
);
1180 } else if
(w
>= backend_first_dvi_whatsit
) {
1181 copy_node_wrapup_dvi
(p
,r
);
1183 copy_node_wrapup_core
(p
,r
);
1192 #define free_sub_list
(source
) if
(source
!= null
) flush_node_list
(source
);
1193 #define free_sub_node
(source
) if
(source
!= null
) flush_node
(source
);
1197 static void flush_node_wrapup_core
(halfword p
)
1199 switch
(subtype
(p
)) {
1206 delete_token_ref
(write_tokens
(p
));
1211 case user_defined_node
:
1212 switch
(user_node_type
(p
)) {
1214 delete_attribute_ref
(user_node_value
(p
));
1219 flush_node_list
(user_node_value
(p
));
1222 /* |delete_string_ref
(user_node_value
(p
));|
*//* if this was mpost ..
*/
1225 delete_token_ref
(user_node_value
(p
));
1229 const char
*hlp
[] = {
1230 "The type of the value in a user defined whatsit node should be one",
1231 "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),",
1232 "or 't' (tokenlist). Yours has an unknown type, and therefore I don't",
1233 "know how to free the node's value. A memory leak may result.",
1236 tex_error
("Unidentified user defined whatsit", hlp
);
1244 void flush_node_wrapup_dvi
(halfword p
)
1248 void flush_node_wrapup_pdf
(halfword p
)
1250 switch
(subtype
(p
)) {
1252 case pdf_restore_node
:
1253 case pdf_refobj_node
:
1254 case pdf_end_link_node
:
1255 case pdf_end_thread_node
:
1257 case pdf_literal_node
:
1258 free_pdf_literal
(p
);
1260 case pdf_colorstack_node
:
1261 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
1262 delete_token_ref
(pdf_colorstack_data
(p
));
1264 case pdf_setmatrix_node
:
1265 delete_token_ref
(pdf_setmatrix_data
(p
));
1267 case pdf_annot_node
:
1268 delete_token_ref
(pdf_annot_data
(p
));
1270 case pdf_link_data_node
:
1272 case pdf_start_link_node
:
1273 if
(pdf_link_attr
(p
) != null
)
1274 delete_token_ref
(pdf_link_attr
(p
));
1275 delete_action_ref
(pdf_link_action
(p
));
1278 if
(pdf_dest_named_id
(p
) > 0)
1279 delete_token_ref
(pdf_dest_id
(p
));
1281 case pdf_action_node
:
1282 if
(pdf_action_type
(p
) == pdf_action_user
) {
1283 delete_token_ref
(pdf_action_tokens
(p
));
1285 if
(pdf_action_file
(p
) != null
)
1286 delete_token_ref
(pdf_action_file
(p
));
1287 if
(pdf_action_type
(p
) == pdf_action_page
)
1288 delete_token_ref
(pdf_action_tokens
(p
));
1289 else if
(pdf_action_named_id
(p
) > 0)
1290 delete_token_ref
(pdf_action_id
(p
));
1293 case pdf_thread_data_node
:
1295 case pdf_thread_node
:
1296 case pdf_start_thread_node
:
1297 if
(pdf_thread_named_id
(p
) > 0)
1298 delete_token_ref
(pdf_thread_id
(p
));
1299 if
(pdf_thread_attr
(p
) != null
)
1300 delete_token_ref
(pdf_thread_attr
(p
));
1305 void flush_node
(halfword p
)
1308 if
(p
== null
) /* legal
, but no-op
*/
1314 free_sub_list
(lig_ptr
(p
));
1317 free_sub_list
(leader_ptr
(p
));
1322 free_sub_list
(list_ptr
(p
));
1325 /* watch the start at temp node hack
*/
1326 free_sub_list
(vlink
(pre_break
(p
)));
1327 free_sub_list
(vlink
(post_break
(p
)));
1328 free_sub_list
(vlink
(no_break
(p
)));
1335 case glue_spec_node
:
1336 /* this allows free-ing of lua-allocated glue specs
*/
1337 //if
(valid_node
(p
)) {
1338 // free_node
(p
, subtype
(p
));
1343 case local_par_node
:
1348 if
(w
>= backend_first_pdf_whatsit
) {
1349 flush_node_wrapup_pdf
(p
);
1350 } else if
(w
>= backend_first_dvi_whatsit
) {
1351 flush_node_wrapup_dvi
(p
);
1353 flush_node_wrapup_core
(p
);
1357 flush_node_list
(ins_ptr
(p
));
1359 case margin_kern_node
:
1360 flush_node
(margin_char
(p
));
1363 delete_token_ref
(mark_ptr
(p
));
1366 flush_node_list
(adjust_ptr
(p
));
1368 case style_node
: /* nothing to do
*/
1371 free_sub_list
(display_mlist
(p
));
1372 free_sub_list
(text_mlist
(p
));
1373 free_sub_list
(script_mlist
(p
));
1374 free_sub_list
(script_script_mlist
(p
));
1377 free_sub_list
(nucleus
(p
));
1378 free_sub_list
(subscr
(p
));
1379 free_sub_list
(supscr
(p
));
1382 free_sub_list
(nucleus
(p
));
1383 free_sub_list
(subscr
(p
));
1384 free_sub_list
(supscr
(p
));
1385 free_sub_node
(left_delimiter
(p
));
1386 free_sub_list
(degree
(p
));
1389 free_sub_list
(nucleus
(p
));
1390 free_sub_list
(subscr
(p
));
1391 free_sub_list
(supscr
(p
));
1392 free_sub_list
(top_accent_chr
(p
));
1393 free_sub_list
(bot_accent_chr
(p
));
1394 free_sub_list
(overlay_accent_chr
(p
));
1397 free_sub_list
(delimiter
(p
));
1399 case delim_node
: /* nothing to do
*/
1400 case math_char_node
:
1401 case math_text_char_node
:
1404 case sub_mlist_node
:
1405 free_sub_list
(math_list
(p
));
1408 free_sub_list
(numerator
(p
));
1409 free_sub_list
(denominator
(p
));
1410 free_sub_node
(left_delimiter
(p
));
1411 free_sub_node
(right_delimiter
(p
));
1413 case pseudo_file_node
:
1414 free_sub_list
(pseudo_lines
(p
));
1416 case pseudo_line_node
:
1418 free_node
(p
, subtype
(p
));
1421 case align_stack_node
:
1426 case unhyphenated_node
:
1427 case hyphenated_node
:
1430 case inserting_node
:
1433 case attribute_node
:
1434 case attribute_list_node
:
1438 formatted_error
("nodes","flushing weird node type %d", type
(p
));
1441 if
(nodetype_has_attributes
(type
(p
))) {
1442 delete_attribute_ref
(node_attr
(p
));
1443 lua_properties_reset
(p
);
1445 free_node
(p
, get_node_size
(type
(p
), subtype
(p
)));
1450 void flush_node_list
(halfword pp
)
1451 { /* erase list of nodes starting at |p|
*/
1452 register halfword p
= pp
;
1453 if
(p
== null
) /* legal
, but no-op
*/
1457 lua_properties_push
; /* saves stack and time
*/
1459 register halfword q
= vlink
(p
);
1463 lua_properties_pop
; /* saves stack and time
*/
1467 static void check_node_wrapup_core
(halfword p
)
1469 switch
(subtype
(p
)) {
1472 check_token_ref
(write_tokens
(p
));
1474 case user_defined_node
:
1475 switch
(user_node_type
(p
)) {
1477 check_attribute_ref
(user_node_value
(p
));
1480 check_token_ref
(user_node_value
(p
));
1483 dorangetest
(p
, user_node_value
(p
), var_mem_max
);
1489 confusion
("unknown user node type");
1501 void check_node_wrapup_dvi
(halfword p
)
1505 void check_node_wrapup_pdf
(halfword p
)
1507 switch
(subtype
(p
)) {
1508 case pdf_literal_node
:
1509 if
(pdf_literal_type
(p
) == normal
)
1510 check_token_ref
(pdf_literal_data
(p
));
1512 case pdf_colorstack_node
:
1513 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
1514 check_token_ref
(pdf_colorstack_data
(p
));
1516 case pdf_setmatrix_node
:
1517 check_token_ref
(pdf_setmatrix_data
(p
));
1520 if
(late_lua_name
(p
) > 0)
1521 check_token_ref
(late_lua_name
(p
));
1522 if
(late_lua_type
(p
) == normal
)
1523 check_token_ref
(late_lua_data
(p
));
1525 case pdf_annot_node
:
1526 check_token_ref
(pdf_annot_data
(p
));
1528 case pdf_start_link_node
:
1529 if
(pdf_link_attr
(p
) != null
)
1530 check_token_ref
(pdf_link_attr
(p
));
1531 check_action_ref
(pdf_link_action
(p
));
1534 if
(pdf_dest_named_id
(p
) > 0)
1535 check_token_ref
(pdf_dest_id
(p
));
1537 case pdf_thread_node
:
1538 case pdf_start_thread_node
:
1539 if
(pdf_thread_named_id
(p
) > 0)
1540 check_token_ref
(pdf_thread_id
(p
));
1541 if
(pdf_thread_attr
(p
) != null
)
1542 check_token_ref
(pdf_thread_attr
(p
));
1545 case pdf_restore_node
:
1546 case pdf_refobj_node
:
1547 case pdf_end_link_node
:
1548 case pdf_end_thread_node
:
1551 confusion
("wrapup pdf nodes");
1556 void check_node
(halfword p
)
1561 dorangetest
(p
, lig_ptr
(p
), var_mem_max
);
1564 dorangetest
(p
, leader_ptr
(p
), var_mem_max
);
1569 case align_record_node
:
1570 dorangetest
(p
, list_ptr
(p
), var_mem_max
);
1573 dorangetest
(p
, ins_ptr
(p
), var_mem_max
);
1577 if
(w
>= backend_first_pdf_whatsit
) {
1578 check_node_wrapup_pdf
(p
);
1579 } else if
(w
>= backend_first_dvi_whatsit
) {
1580 check_node_wrapup_dvi
(p
);
1582 check_node_wrapup_core
(p
);
1585 case margin_kern_node
:
1586 check_node
(margin_char
(p
));
1591 dorangetest
(p
, vlink
(pre_break
(p
)), var_mem_max
);
1592 dorangetest
(p
, vlink
(post_break
(p
)), var_mem_max
);
1593 dorangetest
(p
, vlink
(no_break
(p
)), var_mem_max
);
1596 dorangetest
(p
, adjust_ptr
(p
), var_mem_max
);
1598 case pseudo_file_node
:
1599 dorangetest
(p
, pseudo_lines
(p
), var_mem_max
);
1601 case pseudo_line_node
:
1605 dorangetest
(p
, display_mlist
(p
), var_mem_max
);
1606 dorangetest
(p
, text_mlist
(p
), var_mem_max
);
1607 dorangetest
(p
, script_mlist
(p
), var_mem_max
);
1608 dorangetest
(p
, script_script_mlist
(p
), var_mem_max
);
1611 dorangetest
(p
, numerator
(p
), var_mem_max
);
1612 dorangetest
(p
, denominator
(p
), var_mem_max
);
1613 dorangetest
(p
, left_delimiter
(p
), var_mem_max
);
1614 dorangetest
(p
, right_delimiter
(p
), var_mem_max
);
1617 dorangetest
(p
, nucleus
(p
), var_mem_max
);
1618 dorangetest
(p
, subscr
(p
), var_mem_max
);
1619 dorangetest
(p
, supscr
(p
), var_mem_max
);
1622 dorangetest
(p
, nucleus
(p
), var_mem_max
);
1623 dorangetest
(p
, subscr
(p
), var_mem_max
);
1624 dorangetest
(p
, supscr
(p
), var_mem_max
);
1625 dorangetest
(p
, degree
(p
), var_mem_max
);
1626 dorangetest
(p
, left_delimiter
(p
), var_mem_max
);
1629 dorangetest
(p
, nucleus
(p
), var_mem_max
);
1630 dorangetest
(p
, subscr
(p
), var_mem_max
);
1631 dorangetest
(p
, supscr
(p
), var_mem_max
);
1632 dorangetest
(p
, top_accent_chr
(p
), var_mem_max
);
1633 dorangetest
(p
, bot_accent_chr
(p
), var_mem_max
);
1634 dorangetest
(p
, overlay_accent_chr
(p
), var_mem_max
);
1637 dorangetest
(p
, delimiter
(p
), var_mem_max
);
1645 case attribute_list_node
:
1646 case attribute_node
:
1647 case glue_spec_node
:
1649 case align_stack_node
:
1654 case unhyphenated_node
:
1655 case hyphenated_node
:
1661 case local_par_node
:
1664 fprintf
(stdout
, "check_node: type is %d\n", type
(p
));
1670 void fix_node_list
(halfword head
)
1685 halfword get_node
(int s
)
1687 register halfword r
;
1689 if
(s
< MAX_CHAIN_SIZE
) {
1692 free_chain
[s
] = vlink
(r
);
1693 #ifdef CHECK_NODE_USAGE
1694 varmem_sizes
[r
] = (char
) s
;
1697 var_used
+= s
; /* maintain usage statistics
*/
1700 /* this is the end of the 'inner loop'
*/
1701 return slow_get_node
(s
);
1703 normal_error
("nodes","there is a problem in getting a node, case 1");
1709 void free_node
(halfword p
, int s
)
1711 if
(p
<= my_prealloc
) {
1712 formatted_error
("nodes", "node number %d of type %d should not be freed", (int
) p
, type
(p
));
1715 #ifdef CHECK_NODE_USAGE
1716 varmem_sizes
[p
] = 0;
1718 if
(s
< MAX_CHAIN_SIZE
) {
1719 vlink
(p
) = free_chain
[s
];
1722 /* todo ? it is perhaps possible to merge this node with an existing rover
*/
1725 while
(vlink
(rover
) != vlink
(p
)) {
1726 rover
= vlink
(rover
);
1730 /* maintain statistics
*/
1735 static void free_node_chain
(halfword q
, int s
)
1737 register halfword p
= q
;
1738 while
(vlink
(p
) != null
) {
1739 #ifdef CHECK_NODE_USAGE
1740 varmem_sizes
[p
] = 0;
1746 #ifdef CHECK_NODE_USAGE
1747 varmem_sizes
[p
] = 0;
1749 vlink
(p
) = free_chain
[s
];
1754 void init_node_mem
(int t
)
1756 my_prealloc
= var_mem_stat_max
;
1760 assert
(whatsit_node_data
[user_defined_node
].id
== user_defined_node
);
1761 assert
(node_data
[passive_node
].id
== passive_node
);
1764 varmem
= (memory_word
*) realloc
((void
*) varmem
, sizeof
(memory_word
) * (unsigned
) t
);
1765 if
(varmem
== NULL) {
1766 overflow
("node memory size", (unsigned
) var_mem_max
);
1768 memset
((void
*) (varmem
), 0, (unsigned
) t
* sizeof
(memory_word
));
1769 #ifdef CHECK_NODE_USAGE
1770 varmem_sizes
= (char
*) realloc
(varmem_sizes
, sizeof
(char
) * (unsigned
) t
);
1771 if
(varmem_sizes
== NULL) {
1772 overflow
("node memory size", (unsigned
) var_mem_max
);
1774 memset
((void
*) varmem_sizes
, 0, sizeof
(char
) * (unsigned
) t
);
1777 rover
= var_mem_stat_max
+ 1;
1778 vlink
(rover
) = rover
;
1779 node_size
(rover
) = (t
- rover
);
1781 /* initialize static glue specs
*/
1782 width
(zero_glue
) = 0;
1783 type
(zero_glue
) = glue_spec_node
;
1784 vlink
(zero_glue
) = null
;
1785 stretch
(zero_glue
) = 0;
1786 stretch_order
(zero_glue
) = normal
;
1787 shrink
(zero_glue
) = 0;
1788 shrink_order
(zero_glue
) = normal
;
1789 width
(sfi_glue
) = 0;
1790 type
(sfi_glue
) = glue_spec_node
;
1791 vlink
(sfi_glue
) = null
;
1792 stretch
(sfi_glue
) = 0;
1793 stretch_order
(sfi_glue
) = sfi
;
1794 shrink
(sfi_glue
) = 0;
1795 shrink_order
(sfi_glue
) = normal
;
1796 width
(fil_glue
) = 0;
1797 type
(fil_glue
) = glue_spec_node
;
1798 vlink
(fil_glue
) = null
;
1799 stretch
(fil_glue
) = unity
;
1800 stretch_order
(fil_glue
) = fil
;
1801 shrink
(fil_glue
) = 0;
1802 shrink_order
(fil_glue
) = normal
;
1803 width
(fill_glue
) = 0;
1804 type
(fill_glue
) = glue_spec_node
;
1805 vlink
(fill_glue
) = null
;
1806 stretch
(fill_glue
) = unity
;
1807 stretch_order
(fill_glue
) = fill
;
1808 shrink
(fill_glue
) = 0;
1809 shrink_order
(fill_glue
) = normal
;
1811 type
(ss_glue
) = glue_spec_node
;
1812 vlink
(ss_glue
) = null
;
1813 stretch
(ss_glue
) = unity
;
1814 stretch_order
(ss_glue
) = fil
;
1815 shrink
(ss_glue
) = unity
;
1816 shrink_order
(ss_glue
) = fil
;
1817 width
(fil_neg_glue
) = 0;
1818 type
(fil_neg_glue
) = glue_spec_node
;
1819 vlink
(fil_neg_glue
) = null
;
1820 stretch
(fil_neg_glue
) = -unity
;
1821 stretch_order
(fil_neg_glue
) = fil
;
1822 shrink
(fil_neg_glue
) = 0;
1823 shrink_order
(fil_neg_glue
) = normal
;
1824 /* initialize node list heads
*/
1825 vinfo
(page_ins_head
) = 0;
1826 type
(page_ins_head
) = temp_node
;
1827 vlink
(page_ins_head
) = null
;
1828 alink
(page_ins_head
) = null
;
1829 vinfo
(contrib_head
) = 0;
1830 type
(contrib_head
) = temp_node
;
1831 vlink
(contrib_head
) = null
;
1832 alink
(contrib_head
) = null
;
1833 vinfo
(page_head
) = 0;
1834 type
(page_head
) = temp_node
;
1835 vlink
(page_head
) = null
;
1836 alink
(page_head
) = null
;
1837 vinfo
(temp_head
) = 0;
1838 type
(temp_head
) = temp_node
;
1839 vlink
(temp_head
) = null
;
1840 alink
(temp_head
) = null
;
1841 vinfo
(hold_head
) = 0;
1842 type
(hold_head
) = temp_node
;
1843 vlink
(hold_head
) = null
;
1844 alink
(hold_head
) = null
;
1845 vinfo
(adjust_head
) = 0;
1846 type
(adjust_head
) = temp_node
;
1847 vlink
(adjust_head
) = null
;
1848 alink
(adjust_head
) = null
;
1849 vinfo
(pre_adjust_head
) = 0;
1850 type
(pre_adjust_head
) = temp_node
;
1851 vlink
(pre_adjust_head
) = null
;
1852 alink
(pre_adjust_head
) = null
;
1854 type
(active
) = unhyphenated_node
;
1855 vlink
(active
) = null
;
1856 alink
(active
) = null
;
1857 vinfo
(align_head
) = 0;
1858 type
(align_head
) = temp_node
;
1859 vlink
(align_head
) = null
;
1860 alink
(align_head
) = null
;
1861 vinfo
(end_span
) = 0;
1862 type
(end_span
) = span_node
;
1863 vlink
(end_span
) = null
;
1864 alink
(end_span
) = null
;
1865 type
(begin_point
) = glyph_node
;
1866 subtype
(begin_point
) = 0;
1867 vlink
(begin_point
) = null
;
1868 vinfo
(begin_point
+ 1) = null
;
1869 alink
(begin_point
) = null
;
1870 font
(begin_point
) = 0;
1871 character
(begin_point
) = '.'
;
1872 vinfo
(begin_point
+ 3) = 0;
1873 vlink
(begin_point
+ 3) = 0;
1874 vinfo
(begin_point
+ 4) = 0;
1875 vlink
(begin_point
+ 4) = 0;
1876 type
(end_point
) = glyph_node
;
1877 subtype
(end_point
) = 0;
1878 vlink
(end_point
) = null
;
1879 vinfo
(end_point
+ 1) = null
;
1880 alink
(end_point
) = null
;
1881 font
(end_point
) = 0;
1882 character
(end_point
) = '.'
;
1883 vinfo
(end_point
+ 3) = 0;
1884 vlink
(end_point
+ 3) = 0;
1885 vinfo
(end_point
+ 4) = 0;
1886 vlink
(end_point
+ 4) = 0;
1890 void dump_node_mem
(void
)
1892 dump_int
(var_mem_max
);
1894 dump_things
(varmem
[0], var_mem_max
);
1895 #ifdef CHECK_NODE_USAGE
1896 dump_things
(varmem_sizes
[0], var_mem_max
);
1898 dump_things
(free_chain
[0], MAX_CHAIN_SIZE
);
1900 dump_int
(my_prealloc
);
1903 @ it makes sense to enlarge the varmem array immediately
1906 void undump_node_mem
(void
)
1911 var_mem_max
= (x
< 100000 ?
100000 : x
);
1912 varmem
= xmallocarray
(memory_word
, (unsigned
) var_mem_max
);
1913 undump_things
(varmem
[0], x
);
1914 #ifdef CHECK_NODE_USAGE
1915 varmem_sizes
= xmallocarray
(char
, (unsigned
) var_mem_max
);
1916 memset
((void
*) varmem_sizes
, 0, (unsigned
) var_mem_max
* sizeof
(char
));
1917 undump_things
(varmem_sizes
[0], x
);
1919 undump_things
(free_chain
[0], MAX_CHAIN_SIZE
);
1920 undump_int
(var_used
);
1921 undump_int
(my_prealloc
);
1922 if
(var_mem_max
> x
) {
1923 /* todo ? it is perhaps possible to merge the new node with an existing rover
*/
1925 node_size
(x
) = (var_mem_max
- x
);
1926 while
(vlink
(rover
) != vlink
(x
)) {
1927 rover
= vlink
(rover
);
1934 halfword slow_get_node
(int s
)
1939 t
= node_size
(rover
);
1940 if
(vlink
(rover
) < var_mem_max
&& vlink(rover) != 0) {
1942 /* allocating from the bottom helps decrease page faults
*/
1943 register halfword r
= rover
;
1945 vlink
(rover
) = vlink
(r
);
1946 node_size
(rover
) = node_size
(r
) - s
;
1947 if
(vlink
(rover
) != r
) { /* list is longer than one
*/
1949 while
(vlink
(q
) != r
) {
1956 if
(vlink
(rover
) < var_mem_max
) {
1957 #ifdef CHECK_NODE_USAGE
1958 varmem_sizes
[r
] = (char
) (s
> 127 ?
127 : s
);
1961 var_used
+= s
; /* maintain usage statistics
*/
1962 return r
; /* this is the only exit
*/
1964 normal_error
("nodes","there is a problem in getting a node, case 2");
1968 /* attempt to keep the free list small
*/
1970 if
(vlink
(rover
) != rover
) {
1971 if
(t
< MAX_CHAIN_SIZE
) {
1972 halfword l
= vlink
(rover
);
1973 vlink
(rover
) = free_chain
[t
];
1974 free_chain
[t
] = rover
;
1976 while
(vlink
(l
) != free_chain
[t
]) {
1983 while
(vlink
(rover
) != l
) {
1984 if
(node_size
(rover
) > s
) {
1987 rover
= vlink
(rover
);
1991 /* if we are still here
, it was apparently impossible to get a match
*/
1992 x
= (var_mem_max
>> 2) + s
;
1993 varmem
= (memory_word
*) realloc
((void
*) varmem
, sizeof
(memory_word
) * (unsigned
) (var_mem_max
+ x
));
1994 if
(varmem
== NULL) {
1995 overflow
("node memory size", (unsigned
) var_mem_max
);
1997 memset
((void
*) (varmem
+ var_mem_max
), 0, (unsigned
) x
* sizeof
(memory_word
));
1998 #ifdef CHECK_NODE_USAGE
1999 varmem_sizes
= (char
*) realloc
(varmem_sizes
, sizeof
(char
) * (unsigned
) (var_mem_max
+ x
));
2000 if
(varmem_sizes
== NULL) {
2001 overflow
("node memory size", (unsigned
) var_mem_max
);
2003 memset
((void
*) (varmem_sizes
+ var_mem_max
), 0, (unsigned
) (x
) * sizeof
(char
));
2005 /* todo ? it is perhaps possible to merge the new memory with an existing rover
*/
2006 vlink
(var_mem_max
) = rover
;
2007 node_size
(var_mem_max
) = x
;
2008 while
(vlink
(rover
) != vlink
(var_mem_max
)) {
2009 rover
= vlink
(rover
);
2011 vlink
(rover
) = var_mem_max
;
2012 rover
= var_mem_max
;
2017 normal_error
("nodes","there is a problem in getting a node, case 3");
2023 char
*sprint_node_mem_usage
(void
)
2026 #ifdef CHECK_NODE_USAGE
2031 int node_counts
[last_normal_node
+ last_whatsit_node
+ 2] = { 0 };
2033 for
(i
= (var_mem_max
- 1); i
> my_prealloc
; i--
) {
2034 if
(varmem_sizes
[i
] > 0) {
2035 if
(type
(i
) > last_normal_node
+ last_whatsit_node
) {
2036 node_counts
[last_normal_node
+ last_whatsit_node
+ 1]++;
2037 } else if
(type
(i
) == whatsit_node
) {
2038 node_counts
[(subtype
(i
) + last_normal_node
+ 1)]++;
2040 node_counts
[type
(i
)]++;
2044 for
(i
= 0; i
< last_normal_node
+ last_whatsit_node
+ 2; i
++) {
2045 if
(node_counts
[i
] > 0) {
2047 (i
> (last_normal_node
+ 1) ?
(i
- last_normal_node
- 1) : 0);
2048 snprintf
(msg
, 255, "%s%d %s", (b ?
", " : ""), (int
) node_counts
[i
],
2049 get_node_name
((i
> last_normal_node ? whatsit_node
: i
), j
));
2050 ss
= xmalloc
((unsigned
) (strlen
(s
) + strlen
(msg
) + 1));
2065 halfword list_node_mem_usage
(void
)
2068 #ifdef CHECK_NODE_USAGE
2071 char
*saved_varmem_sizes
= xmallocarray
(char
, (unsigned
) var_mem_max
);
2072 memcpy
(saved_varmem_sizes
, varmem_sizes
, (size_t
) var_mem_max
);
2073 for
(i
= my_prealloc
+ 1; i
< (var_mem_max
- 1); i
++) {
2074 if
(saved_varmem_sizes
[i
] > 0) {
2084 free
(saved_varmem_sizes
);
2090 void print_node_mem_stats
(void
)
2096 int free_chain_counts
[MAX_CHAIN_SIZE
] = { 0 };
2097 snprintf
(msg
, 255, " %d words of node memory still in use:", (int
) (var_used
+ my_prealloc
));
2099 s
= sprint_node_mem_usage
();
2104 tprint_nl
(" avail lists: ");
2106 for
(i
= 1; i
< MAX_CHAIN_SIZE
; i
++) {
2107 for
(j
= free_chain
[i
]; j
!= null
; j
= vlink
(j
))
2108 free_chain_counts
[i
]++;
2109 if
(free_chain_counts
[i
] > 0) {
2110 snprintf
(msg
, 255, "%s%d:%d", (b ?
"," : ""), i
, (int
) free_chain_counts
[i
]);
2115 /* newline
, if needed
*/
2119 /* this belongs in the web but i couldn't find the correct syntactic place
*/
2121 halfword new_span_node
(halfword n
, int s
, scaled w
)
2123 halfword p
= new_node
(span_node
, 0);
2133 static halfword new_attribute_node
(unsigned int i
, int v
)
2135 register halfword r
= get_node
(attribute_node_size
);
2136 type
(r
) = attribute_node
;
2137 attribute_id
(r
) = (halfword
) i
;
2138 attribute_value
(r
) = v
;
2139 /* not used but nicer in print
*/
2145 halfword copy_attribute_list
(halfword n
)
2147 halfword q
= get_node
(attribute_node_size
);
2148 register halfword p
= q
;
2149 type
(p
) = attribute_list_node
;
2150 attr_list_ref
(p
) = 0;
2153 register halfword r
= get_node
(attribute_node_size
);
2154 /* the link will be fixed automatically in the next loop
*/
2155 (void
) memcpy
((void
*) (varmem
+ r
), (void
*) (varmem
+ n
),
2156 (sizeof
(memory_word
) * attribute_node_size
));
2165 void update_attribute_cache
(void
)
2169 attr_list_cache
= get_node
(attribute_node_size
);
2170 type
(attr_list_cache
) = attribute_list_node
;
2171 attr_list_ref
(attr_list_cache
) = 0;
2172 p
= attr_list_cache
;
2173 for
(i
= 0; i
<= max_used_attr
; i
++) {
2174 register int v
= attribute
(i
);
2175 if
(v
> UNUSED_ATTRIBUTE
) {
2176 register halfword r
= new_attribute_node
((unsigned
) i
, v
);
2181 if
(vlink
(attr_list_cache
) == null
) {
2182 free_node
(attr_list_cache
, attribute_node_size
);
2183 attr_list_cache
= null
;
2189 void build_attribute_list
(halfword b
)
2191 if
(max_used_attr
>= 0) {
2192 if
(attr_list_cache
== cache_disabled|| attr_list_cache
== null
) {
2193 update_attribute_cache
();
2194 if
(attr_list_cache
== null
)
2197 attr_list_ref
(attr_list_cache
)++;
2198 node_attr
(b
) = attr_list_cache
;
2203 halfword current_attribute_list
(void
)
2205 if
(max_used_attr
>= 0) {
2206 if
(attr_list_cache
== cache_disabled
) {
2207 update_attribute_cache
();
2209 return attr_list_cache
;
2216 void reassign_attribute
(halfword n
, halfword new
)
2221 /* there is nothing to assign but we need to check for an old value
*/
2223 delete_attribute_ref
(old
); /* also nulls attr field of n
*/
2224 } else if
(old
== null
) {
2225 /* nothing is assigned so we just do that now
*/
2226 assign_attribute_ref
(n
,new
);
2227 } else if
(old
!= new
) {
2228 /* something is assigned so we need to clean up and assign then
*/
2229 delete_attribute_ref
(old
);
2230 assign_attribute_ref
(n
,new
);
2232 /* else
: same value so there is no need to assign and change the refcount
*/
2233 node_attr
(n
) = new
;
2237 void delete_attribute_ref
(halfword b
)
2240 if
(type
(b
) == attribute_list_node
){
2242 if
(attr_list_ref
(b
) == 0) {
2243 if
(b
== attr_list_cache
)
2244 attr_list_cache
= cache_disabled
;
2245 free_node_chain
(b
, attribute_node_size
);
2247 /* maintain sanity
*/
2248 if
(attr_list_ref
(b
) < 0) {
2249 attr_list_ref
(b
) = 0;
2252 normal_error
("nodes","trying to delete an attribute reference of a non attribute node");
2257 void reset_node_properties
(halfword b
)
2260 lua_properties_reset
(b
);
2264 @ |p| is an attr list head
, or zero
2266 halfword do_set_attribute
(halfword p
, int i
, int val
)
2268 register halfword q
;
2270 if
(p
== null
) { /* add a new head \
& node */
2271 q
= get_node
(attribute_node_size
);
2272 type
(q
) = attribute_list_node
;
2273 attr_list_ref
(q
) = 1;
2274 p
= new_attribute_node
((unsigned
) i
, val
);
2279 if
(vlink
(p
) != null
) {
2280 while
(vlink
(p
) != null
) {
2281 int t
= attribute_id
(vlink
(p
));
2282 if
(t
== i
&& attribute_value(vlink(p)) == val)
2283 return q
; /* no need to do anything
*/
2293 if
(attribute_id
(vlink
(p
)) == i
) {
2294 attribute_value
(vlink
(p
)) = val
;
2295 } else
{ /* add a new node
*/
2296 halfword r
= new_attribute_node
((unsigned
) i
, val
);
2297 vlink
(r
) = vlink
(p
);
2302 normal_error
("nodes","trying to set an attribute fails, case 1");
2308 void set_attribute
(halfword n
, int i
, int val
)
2310 register halfword p
;
2312 /* not all nodes can have an attribute list
*/
2313 if
(!nodetype_has_attributes
(type
(n
)))
2315 /* if we have no list
, we create one and quit
*/
2317 if
(p
== null
) { /* add a new head \
& node */
2318 p
= get_node
(attribute_node_size
);
2319 type
(p
) = attribute_list_node
;
2320 attr_list_ref
(p
) = 1;
2322 p
= new_attribute_node
((unsigned
) i
, val
);
2323 vlink
(node_attr
(n
)) = p
;
2326 /* we check if we have this attribute already and quit if the value stays the same
*/
2327 if
(vlink
(p
) != null
) {
2328 while
(vlink
(p
) != null
) {
2329 int t
= attribute_id
(vlink
(p
));
2330 if
(t
== i
&& attribute_value(vlink(p)) == val)
2337 /* j has now the position
(if found
) .. we assume a sorted list
! */
2340 if
(attr_list_ref
(p
) == 0 ) {
2341 /* the list is invalid i.e. freed already
*/
2342 formatted_warning
("nodes","node %d has an attribute list that is free already, case 1",(int
) n
);
2343 /* the still dangling list gets ref count
1 */
2344 attr_list_ref
(p
) = 1;
2345 } else if
(attr_list_ref
(p
) == 1) {
2346 /* this can really happen HH-LS
*/
2347 if
(p
== attr_list_cache
) {
2348 /* we can invalidate the cache setting
*/
2349 /* attr_list_cache
= cache_disabled
*/
2350 /* or save the list
, as done below
*/
2351 p
= copy_attribute_list
(p
);
2353 /* the copied list gets ref count
1 */
2354 attr_list_ref
(p
) = 1;
2357 /* the list is used multiple times so we make a copy
*/
2358 p
= copy_attribute_list
(p
);
2359 /* we decrement the ref count or the original
*/
2360 delete_attribute_ref
(node_attr
(n
));
2362 /* the copied list gets ref count
1 */
2363 attr_list_ref
(p
) = 1;
2367 /* we go to position j in the list
*/
2370 /* if we have a hit we just set the value otherwise we add a new node
*/
2371 if
(attribute_id
(vlink
(p
)) == i
) {
2372 attribute_value
(vlink
(p
)) = val
;
2373 } else
{ /* add a new node
*/
2374 halfword r
= new_attribute_node
((unsigned
) i
, val
);
2375 vlink
(r
) = vlink
(p
);
2379 normal_error
("nodes","trying to set an attribute fails, case 2");
2384 int unset_attribute
(halfword n
, int i
, int val
)
2386 register halfword p
;
2390 if
(!nodetype_has_attributes
(type
(n
)))
2394 return UNUSED_ATTRIBUTE
;
2395 if
(attr_list_ref
(p
) == 0) {
2396 formatted_warning
("nodes","node %d has an attribute list that is free already, case 2", (int
) n
);
2397 return UNUSED_ATTRIBUTE
;
2399 if
(vlink
(p
) != null
) {
2400 while
(vlink
(p
) != null
) {
2401 t
= attribute_id
(vlink
(p
));
2403 return UNUSED_ATTRIBUTE
;
2411 if
(attribute_id
(p
) != i
)
2412 return UNUSED_ATTRIBUTE
;
2413 /* if we are still here
, the attribute exists
*/
2415 if
(attr_list_ref
(p
) > 1 || p
== attr_list_cache
) {
2416 halfword q
= copy_attribute_list
(p
);
2417 if
(attr_list_ref
(p
) > 1) {
2418 delete_attribute_ref
(node_attr
(n
));
2420 attr_list_ref
(q
) = 1;
2423 p
= vlink
(node_attr
(n
));
2426 t
= attribute_value
(p
);
2427 if
(val
== UNUSED_ATTRIBUTE || t
== val
) {
2428 attribute_value
(p
) = UNUSED_ATTRIBUTE
;
2432 normal_error
("nodes","trying to unset an attribute fails");
2438 int has_attribute
(halfword n
, int i
, int val
)
2440 register halfword p
;
2441 if
(!nodetype_has_attributes
(type
(n
)))
2442 return UNUSED_ATTRIBUTE
;
2444 if
(p
== null || vlink
(p
) == null
)
2445 return UNUSED_ATTRIBUTE
;
2448 if
(attribute_id
(p
) == i
) {
2449 int ret
= attribute_value
(p
);
2450 if
(val
== UNUSED_ATTRIBUTE || val
== ret
)
2452 return UNUSED_ATTRIBUTE
;
2453 } else if
(attribute_id
(p
) > i
) {
2454 return UNUSED_ATTRIBUTE
;
2458 return UNUSED_ATTRIBUTE
;
2462 void print_short_node_contents
(halfword p
)
2479 if
(! glue_is_zero
(p
))
2486 short_display
(vlink
(pre_break
(p
)));
2487 short_display
(vlink
(post_break
(p
)));
2493 static void show_pdftex_whatsit_rule_spec
(int p
)
2496 print_rule_dimen
(height
(p
));
2498 print_rule_dimen
(depth
(p
));
2500 print_rule_dimen
(width
(p
));
2503 @ Each new type of node that appears in our data structure must be capable
2504 of being displayed
, copied
, destroyed
, and so on. The routines that we
2505 need for write-oriented whatsits are somewhat like those for mark nodes
;
2506 other extensions might
, of course
, involve more subtlety here.
2509 static void print_write_whatsit
(const char
*s
, pointer p
)
2512 if
(write_stream
(p
) < 16)
2513 print_int
(write_stream
(p
));
2514 else if
(write_stream
(p
) == 16)
2521 static void show_node_wrapup_core
(int p
)
2523 switch
(subtype
(p
)) {
2525 print_write_whatsit
("openout", p
);
2527 print_file_name
(open_name
(p
), open_area
(p
), open_ext
(p
));
2530 print_write_whatsit
("write", p
);
2531 print_mark
(write_tokens
(p
));
2534 print_write_whatsit
("closeout", p
);
2537 tprint_esc
("special");
2538 print_mark
(write_tokens
(p
));
2544 tprint_esc
("savepos");
2546 case user_defined_node
:
2547 tprint_esc
("whatsit");
2548 print_int
(user_node_id
(p
));
2550 switch
(user_node_type
(p
)) {
2556 show_node_list
(user_node_value
(p
));
2561 print(user_node_value(p));
2565 print_mark
(user_node_value
(p
));
2567 default
: /* only 'd'
*/
2568 print_int
(user_node_value
(p
));
2575 void show_node_wrapup_dvi
(int p
)
2579 void show_node_wrapup_pdf
(int p
)
2581 switch
(subtype
(p
)) {
2582 case pdf_literal_node
:
2583 show_pdf_literal
(p
);
2585 case pdf_colorstack_node
:
2586 tprint_esc
("pdfcolorstack ");
2587 print_int
(pdf_colorstack_stack
(p
));
2588 switch
(pdf_colorstack_cmd
(p
)) {
2589 case colorstack_set
:
2592 case colorstack_push
:
2595 case colorstack_pop
:
2598 case colorstack_current
:
2602 confusion
("colorstack");
2605 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
2606 print_mark
(pdf_colorstack_data
(p
));
2608 case pdf_setmatrix_node
:
2609 tprint_esc
("pdfsetmatrix");
2610 print_mark
(pdf_setmatrix_data
(p
));
2613 tprint_esc
("pdfsave");
2615 case pdf_restore_node
:
2616 tprint_esc
("pdfrestore");
2618 case pdf_refobj_node
:
2619 tprint_esc
("pdfrefobj");
2620 if
(obj_obj_is_stream
(static_pdf
, pdf_obj_objnum
(p
))) {
2621 if
(obj_obj_stream_attr
(static_pdf
, pdf_obj_objnum
(p
)) != LUA_NOREF
) {
2623 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
,
2624 obj_obj_stream_attr
(static_pdf
, pdf_obj_objnum
(p
)));
2626 tprint
((const char
*) lua_tostring
(Luas
, -1));
2631 if
(obj_obj_is_file
(static_pdf
, pdf_obj_objnum
(p
)))
2633 if
(obj_obj_data
(static_pdf
, pdf_obj_objnum
(p
)) != LUA_NOREF
) {
2634 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
,
2635 obj_obj_data
(static_pdf
, pdf_obj_objnum
(p
)));
2637 tprint
((const char
*) lua_tostring
(Luas
, -1));
2641 case pdf_annot_node
:
2642 tprint_esc
("pdfannot");
2643 show_pdftex_whatsit_rule_spec
(p
);
2644 print_mark
(pdf_annot_data
(p
));
2646 case pdf_start_link_node
:
2647 tprint_esc
("pdfstartlink");
2648 show_pdftex_whatsit_rule_spec
(p
);
2649 if
(pdf_link_attr
(p
) != null
) {
2651 print_mark
(pdf_link_attr
(p
));
2654 if
(pdf_action_type
(pdf_link_action
(p
)) == pdf_action_user
) {
2656 print_mark
(pdf_action_tokens
(pdf_link_action
(p
)));
2659 if
(pdf_action_file
(pdf_link_action
(p
)) != null
) {
2661 print_mark
(pdf_action_file
(pdf_link_action
(p
)));
2663 switch
(pdf_action_type
(pdf_link_action
(p
))) {
2664 case pdf_action_goto
:
2665 if
(pdf_action_named_id
(pdf_link_action
(p
)) > 0) {
2666 tprint
(" goto name");
2667 print_mark
(pdf_action_id
(pdf_link_action
(p
)));
2669 tprint
(" goto num");
2670 print_int
(pdf_action_id
(pdf_link_action
(p
)));
2673 case pdf_action_page
:
2675 print_int
(pdf_action_id
(pdf_link_action
(p
)));
2676 print_mark
(pdf_action_tokens
(pdf_link_action
(p
)));
2678 case pdf_action_thread
:
2679 if
(pdf_action_named_id
(pdf_link_action
(p
)) > 0) {
2680 tprint
(" thread name");
2681 print_mark
(pdf_action_id
(pdf_link_action
(p
)));
2683 tprint
(" thread num");
2684 print_int
(pdf_action_id
(pdf_link_action
(p
)));
2688 normal_error
("pdf backend", "unknown action type for link");
2692 case pdf_end_link_node
:
2693 tprint_esc
("pdfendlink");
2696 tprint_esc
("pdfdest");
2697 if
(pdf_dest_named_id
(p
) > 0) {
2699 print_mark
(pdf_dest_id
(p
));
2702 print_int
(pdf_dest_id
(p
));
2705 switch
(pdf_dest_type
(p
)) {
2708 if
(pdf_dest_xyz_zoom
(p
) != null
) {
2710 print_int
(pdf_dest_xyz_zoom
(p
));
2713 case pdf_dest_fitbh
:
2716 case pdf_dest_fitbv
:
2730 show_pdftex_whatsit_rule_spec
(p
);
2740 case pdf_thread_node
:
2741 case pdf_start_thread_node
:
2742 if
(subtype
(p
) == pdf_thread_node
)
2743 tprint_esc
("pdfthread");
2745 tprint_esc
("pdfstartthread");
2747 print_rule_dimen
(height
(p
));
2749 print_rule_dimen
(depth
(p
));
2751 print_rule_dimen
(width
(p
));
2752 if
(pdf_thread_attr
(p
) != null
) {
2754 print_mark
(pdf_thread_attr
(p
));
2756 if
(pdf_thread_named_id
(p
) > 0) {
2758 print_mark
(pdf_thread_id
(p
));
2761 print_int
(pdf_thread_id
(p
));
2764 case pdf_end_thread_node
:
2765 tprint_esc
("pdfendthread");
2772 @ Now we are ready for |show_node_list| itself. This procedure has been
2773 written to be ``extra robust'' in the sense that it should not crash or get
2774 into a loop even if the data structures have been messed up by bugs in
2775 the rest of the program. You can safely call its parent routine
2776 |show_box
(p
)| for arbitrary values of |p| when you are debugging \TeX.
2777 However
, in the presence of bad data
, the procedure may
2778 fetch a |memory_word| whose variant is different from the way it was stored
;
2779 for example
, it might try to read |mem
[p
].hh| when |mem
[p
]|
2780 contains a scaled integer
, if |p| is a pointer that has been
2781 clobbered or chosen at random.
2784 @ |str_room| need not be checked
; see |show_box|
2786 @ Recursive calls on |show_node_list| therefore use the following pattern
:
2788 #define node_list_display
(A
) do
{ \
2790 show_node_list
(A
); \
2794 /* prints a node list symbolically
*/
2796 void show_node_list
(int p
)
2798 int n
= 0; /* the number of items already printed at this level
*/
2800 real g
; /* a glue ratio
, as a floating point number
*/
2801 if
((int
) cur_length
> depth_threshold
) {
2803 tprint
(" []"); /* indicate that there's been some truncation
*/
2808 print_current_string
(); /* display the nesting history
*/
2809 if
(int_par
(tracing_online_code
) < -2)
2812 if
(n
> breadth_max
) { /* time to stop
*/
2816 /* Display node |p|
*/
2817 if
(is_char_node
(p
)) {
2818 print_font_and_char
(p
);
2819 if
(is_ligature
(p
)) {
2820 /* Display ligature |p|
; */
2821 tprint
(" (ligature ");
2822 if
(is_leftboundary
(p
))
2824 font_in_short_display
= font
(p
);
2825 short_display
(lig_ptr
(p
));
2826 if
(is_rightboundary
(p
))
2835 /* Display box |p|
; */
2836 if
(type
(p
) == hlist_node
)
2838 else if
(type
(p
) == vlist_node
)
2841 tprint_esc
("unset");
2843 print_scaled
(height
(p
));
2845 print_scaled
(depth
(p
));
2847 print_scaled
(width
(p
));
2848 if
(type
(p
) == unset_node
) {
2849 /* Display special fields of the unset node |p|
; */
2850 if
(span_count
(p
) != min_quarterword
) {
2852 print_int
(span_count
(p
) + 1);
2853 tprint
(" columns)");
2855 if
(glue_stretch
(p
) != 0) {
2856 tprint
(", stretch ");
2857 print_glue
(glue_stretch
(p
), glue_order
(p
), NULL);
2859 if
(glue_shrink
(p
) != 0) {
2860 tprint
(", shrink ");
2861 print_glue
(glue_shrink
(p
), glue_sign
(p
), NULL);
2864 /* Display the value of |glue_set
(p
)|
*/
2865 /* The code will have to change in this place if |glue_ratio| is
2866 a structured type instead of an ordinary |real|. Note that this routine
2867 should avoid arithmetic errors even if the |glue_set| field holds an
2868 arbitrary random value. The following code assumes that a properly
2869 formed nonzero |real| number has absolute value $
2^
{20}$ or more when
2870 it is regarded as an integer
; this precaution was adequate to prevent
2871 floating point underflow on the author's computer.
2874 g
= (real
) (glue_set
(p
));
2875 if
((g
!= 0.0) && (glue_sign(p) != normal)) {
2876 tprint
(", glue set ");
2877 if
(glue_sign
(p
) == shrinking
)
2879 if
(g
> 20000.0 || g
< -20000.0) {
2884 print_glue
(20000 * unity
, glue_order
(p
), NULL);
2886 print_glue
(round
(unity
* g
), glue_order
(p
), NULL);
2890 if
(shift_amount
(p
) != 0) {
2891 tprint
(", shifted ");
2892 print_scaled
(shift_amount
(p
));
2894 tprint
(", direction ");
2895 print_dir
(box_dir
(p
));
2897 node_list_display
(list_ptr
(p
)); /* recursive call
*/
2900 /* Display rule |p|
; */
2901 if
(subtype
(p
) == normal_rule
) {
2902 tprint_esc
("rule(");
2903 } else if
(subtype
(p
) == empty_rule
) {
2904 tprint_esc
("norule(");
2905 } else if
(subtype
(p
) == user_rule
) {
2906 tprint_esc
("userrule(");
2907 } else if
(subtype
(p
) == box_rule
) {
2909 } else if
(subtype
(p
) == image_rule
) {
2910 tprint_esc
("image(");
2912 print_rule_dimen
(height
(p
));
2914 print_rule_dimen
(depth
(p
));
2916 print_rule_dimen
(width
(p
));
2919 /* Display insertion |p|
; */
2920 tprint_esc
("insert");
2921 print_int
(subtype
(p
));
2922 tprint
(", natural size ");
2923 print_scaled
(height
(p
));
2925 print_spec
(split_top_ptr
(p
), NULL);
2927 print_scaled
(depth
(p
));
2928 tprint
("); float cost ");
2929 print_int
(float_cost
(p
));
2930 node_list_display
(ins_ptr
(p
)); /* recursive call
*/
2933 if
(dir_dir
(p
) < 0) {
2934 tprint_esc
("enddir");
2936 print_dir
(dir_dir
(p
) + dir_swap
);
2938 tprint_esc
("begindir");
2940 print_dir
(dir_dir
(p
));
2943 case local_par_node
:
2944 tprint_esc
("localpar");
2947 print_current_string
();
2948 tprint_esc
("localinterlinepenalty");
2950 print_int
(local_pen_inter
(p
));
2952 print_current_string
();
2953 tprint_esc
("localbrokenpenalty");
2955 print_int
(local_pen_broken
(p
));
2957 print_current_string
();
2958 tprint_esc
("localleftbox");
2959 if
(local_box_left
(p
) == null
) {
2963 show_node_list
(local_box_left
(p
));
2967 print_current_string
();
2968 tprint_esc
("localrightbox");
2969 if
(local_box_right
(p
) == null
) {
2973 show_node_list
(local_box_right
(p
));
2979 if
(subtype
(p
)==0) {
2980 tprint_esc
("noboundary");
2982 tprint_esc
("boundary");
2984 print_int
(subtype
(p
));
2986 print_int
(boundary_value
(p
));
2991 if
(w
>= backend_first_pdf_whatsit
) {
2992 show_node_wrapup_pdf
(p
);
2993 } else if
(w
>= backend_first_dvi_whatsit
) {
2994 show_node_wrapup_dvi
(p
);
2996 show_node_wrapup_core
(p
);
3000 /* Display glue |p|
; */
3001 if
(subtype
(p
) >= a_leaders
) {
3002 /* Display leaders |p|
; */
3004 switch
(subtype
(p
)) {
3017 normal_warning
("nodes","weird glue leader subtype ignored");
3020 print_spec
(p
, NULL);
3021 node_list_display
(leader_ptr
(p
)); /* recursive call
*/
3024 if
(subtype
(p
) != normal
) {
3026 if
((subtype
(p
) - 1) < thin_mu_skip_code
) {
3027 print_cmd_chr
(assign_glue_cmd
, glue_base
+ (subtype
(p
) - 1));
3028 } else if
(subtype
(p
) < cond_math_glue
) {
3029 print_cmd_chr
(assign_mu_glue_cmd
, glue_base
+ (subtype
(p
) - 1));
3030 } else if
(subtype
(p
) == cond_math_glue
) {
3031 tprint_esc
("nonscript");
3033 tprint_esc
("mskip");
3037 if
(subtype
(p
) != cond_math_glue
) {
3039 if
(subtype
(p
) < cond_math_glue
)
3040 print_spec
(p
, NULL);
3042 print_spec
(p
, "mu");
3046 case margin_kern_node
:
3048 print_scaled
(width
(p
));
3049 if
(subtype
(p
) == left_side
)
3050 tprint
(" (left margin)");
3052 tprint
(" (right margin)");
3055 /* Display kern |p|
; */
3056 /* An ``explicit'' kern value is indicated implicitly by an explicit space.
*/
3057 if
(subtype
(p
) != mu_glue
) {
3059 if
(subtype
(p
) != normal
)
3061 print_scaled
(width
(p
));
3062 if
(subtype
(p
) == accent_kern
)
3063 tprint
(" (for accent)");
3065 tprint_esc
("mkern");
3066 print_scaled
(width
(p
));
3071 /* Display math node |p|
; */
3073 if
(subtype
(p
) == before
)
3077 if
(!glue_is_zero
(p
)) {
3079 print_spec
(p
, NULL);
3080 } else if
(surround
(p
) != 0) {
3081 tprint
(", surrounded ");
3082 print_scaled
(surround
(p
));
3086 /* Display penalty |p|
; */
3087 tprint_esc
("penalty ");
3088 print_int
(penalty
(p
));
3091 /* Display discretionary |p|
; */
3092 /* The |post_break| list of a discretionary node is indicated by a prefixed
3093 `\.
{\char'
174}' instead of the `\..' before the |pre_break| list.
*/
3094 tprint_esc
("discretionary");
3095 print_int
(disc_penalty
(p
));
3097 if
(vlink
(no_break
(p
)) != null
) {
3098 tprint
(" replacing ");
3099 node_list_display
(vlink
(no_break
(p
)));
3101 node_list_display
(vlink
(pre_break
(p
))); /* recursive call
*/
3103 show_node_list
(vlink
(post_break
(p
)));
3104 flush_char
(); /* recursive call
*/
3107 /* Display mark |p|
; */
3109 if
(mark_class
(p
) != 0) {
3111 print_int
(mark_class
(p
));
3113 print_mark
(mark_ptr
(p
));
3116 /* Display adjustment |p|
; */
3117 tprint_esc
("vadjust");
3118 if
(subtype
(p
) != 0)
3120 node_list_display
(adjust_ptr
(p
)); /* recursive call
*/
3122 case glue_spec_node
:
3123 tprint
("<glue_spec ");
3124 print_spec
(p
, NULL);
3136 @ This routine finds the 'base' width of a horizontal box
, using the same logic
3137 that \TeX82 used for \.
{\\predisplaywidth
} */
3140 pointer actual_box_width
(pointer r
, scaled base_width
)
3142 scaled d
; /* increment to |v|
*/
3143 scaled w
= -max_dimen
; /* calculated |size|
*/
3144 scaled v
= shift_amount
(r
) + base_width
; /* |w| plus possible glue amount
*/
3145 pointer p
= list_ptr
(r
); /* current node when calculating |pre_display_size|
*/
3147 if
(is_char_node
(p
)) {
3158 case margin_kern_node
:
3165 /* begin mathskip code
*/
3166 if
(glue_is_zero
(p
)) {
3172 /* end mathskip code
*/
3174 /* We need to be careful that |w|
, |v|
, and |d| do not depend on any |glue_set|
3175 values
, since such values are subject to system-dependent rounding.
3176 System-dependent numbers are not allowed to infiltrate parameters like
3177 |pre_display_size|
, since \TeX82 is supposed to make the same decisions on all
3181 if
(glue_sign
(r
) == stretching
) {
3182 if
((glue_order
(r
) == stretch_order
(p
)) && (stretch(p) != 0))
3184 } else if
(glue_sign
(r
) == shrinking
) {
3185 if
((glue_order
(r
) == shrink_order
(p
)) && (shrink(p) != 0))
3188 if
(subtype
(p
) >= a_leaders
)
3199 if
(v
< max_dimen
) {
3213 halfword tail_of_list
(halfword p
)
3216 while
(vlink
(q
) != null
)
3226 @ Attribute lists need two extra globals to increase processing efficiency.
3227 |max_used_attr| limits the test loop that checks for set attributes
, and
3228 |attr_list_cache| contains a pointer to an already created attribute list. It is
3229 set to the special value |cache_disabled| when the current value can no longer be
3230 trusted
: after an assignment to an attribute register
, and after a group has
3234 int max_used_attr
; /* maximum assigned attribute id
*/
3235 halfword attr_list_cache
;
3237 @ From the computer's standpoint
, \TeX's chief mission is to create
3238 horizontal and vertical lists. We shall now investigate how the elements
3239 of these lists are represented internally as nodes in the dynamic memory.
3241 A horizontal or vertical list is linked together by |link| fields in
3242 the first word of each node. Individual nodes represent boxes
, glue
,
3243 penalties
, or special things like discretionary hyphens
; because of this
3244 variety
, some nodes are longer than others
, and we must distinguish different
3245 kinds of nodes. We do this by putting a `|type|' field in the first word
,
3246 together with the link and an optional `|subtype|'.
3248 @ Character nodes appear only in horizontal lists
, never in vertical lists.
3250 An |hlist_node| stands for a box that was made from a horizontal list.
3251 Each |hlist_node| is seven words long
, and contains the following fields
3252 (in addition to the mandatory |type| and |link|
, which we shall not
3253 mention explicitly when discussing the other node types
): The |height| and
3254 |width| and |depth| are scaled integers denoting the dimensions of the
3255 box. There is also a |shift_amount| field
, a scaled integer indicating
3256 how much this box should be lowered
(if it appears in a horizontal list
),
3257 or how much it should be moved to the right
(if it appears in a vertical
3258 list
). There is a |list_ptr| field
, which points to the beginning of the
3259 list from which this box was fabricated
; if |list_ptr| is |null|
, the box
3260 is empty. Finally
, there are three fields that represent the setting of
3261 the glue
: |glue_set
(p
)| is a word of type |glue_ratio| that represents
3262 the proportionality constant for glue setting
; |glue_sign
(p
)| is
3263 |stretching| or |shrinking| or |normal| depending on whether or not the
3264 glue should stretch or shrink or remain rigid
; and |glue_order
(p
)|
3265 specifies the order of infinity to which glue setting applies
(|normal|
,
3266 |sfi|
, |fil|
, |fill|
, or |filll|
). The |subtype| field is not used.
3268 @ The |new_null_box| function returns a pointer to an |hlist_node| in
3269 which all subfields have the values corresponding to `\.
{\\hbox\
{\
}}'.
3270 The |subtype| field is set to |min_quarterword|
, since that's the desired
3271 |span_count| value if this |hlist_node| is changed to an |unset_node|.
3274 halfword new_null_box
(void
)
3275 { /* creates a new box node
*/
3276 halfword p
= new_node
(hlist_node
, min_quarterword
);
3277 box_dir
(p
) = text_direction
;
3281 @ A |vlist_node| is like an |hlist_node| in all respects except that it
3282 contains a vertical list.
3284 @ A |rule_node| stands for a solid black rectangle
; it has |width|
,
3285 |depth|
, and |height| fields just as in an |hlist_node|. However
, if
3286 any of these dimensions is $
-2^
{30}$
, the actual value will be determined
3287 by running the rule up to the boundary of the innermost enclosing box.
3288 This is called a ``running dimension.'' The |width| is never running in
3289 an hlist
; the |height| and |depth| are never running in a~vlist.
3291 @ A new rule node is delivered by the |new_rule| function. It
3292 makes all the dimensions ``running
,'' so you have to change the
3293 ones that are not allowed to run.
3296 halfword new_rule
(int s
)
3298 halfword p
= new_node
(rule_node
,s
);
3302 @ Insertions are represented by |ins_node| records
, where the |subtype|
3303 indicates the corresponding box number. For example
, `\.
{\\insert
250}'
3304 leads to an |ins_node| whose |subtype| is |
250+min_quarterword|.
3305 The |height| field of an |ins_node| is slightly misnamed
; it actually holds
3306 the natural height plus depth of the vertical list being inserted.
3307 The |depth| field holds the |split_max_depth| to be used in case this
3308 insertion is split
, and the |split_top_ptr| points to the corresponding
3309 |split_top_skip|. The |float_cost| field holds the |floating_penalty| that
3310 will be used if this insertion floats to a subsequent page after a
3311 split insertion of the same class. There is one more field
, the
3312 |ins_ptr|
, which points to the beginning of the vlist for the insertion.
3314 @ A |mark_node| has a |mark_ptr| field that points to the reference count
3315 of a token list that contains the user's \.
{\\mark
} text.
3316 In addition there is a |mark_class| field that contains the mark class.
3318 @ An |adjust_node|
, which occurs only in horizontal lists
,
3319 specifies material that will be moved out into the surrounding
3320 vertical list
; i.e.
, it is used to implement \TeX's `\.
{\\vadjust
}'
3321 operation. The |adjust_ptr| field points to the vlist containing this
3324 @ A |glyph_node|
, which occurs only in horizontal lists
, specifies a
3325 glyph in a particular font
, along with its attribute list. Older
3326 versions of \TeX\ could use token memory for characters
, because the
3327 font
,char combination would fit in a single word
(both values were
3328 required to be strictly less than $
2^
{16}$
). In LuaTeX
, room is
3329 needed for characters that are larger than that
, as well as a pointer
3330 to a potential attribute list
, and the two displacement values.
3332 In turn
, that made the node so large that it made sense to merge
3333 ligature glyphs as well
, as that requires only one extra pointer. A
3334 few extra classes of glyph nodes will be introduced later. The
3335 unification of all those types makes it easier to manipulate lists of
3336 glyphs. The subtype differentiates various glyph kinds.
3338 First
, here is a function that returns a pointer to a glyph node for a given
3339 glyph in a given font. If that glyph doesn't exist
, |null| is returned
3340 instead. Nodes of this subtype are directly created only for accents
3341 and their base
(through |make_accent|
), and math nucleus items
(in the
3342 conversion from |mlist| to |hlist|
).
3345 halfword new_glyph
(int f
, int c
)
3347 halfword p
= null
; /* the new node
*/
3348 if
((f
== 0) ||
(char_exists
(f
, c
))) {
3349 p
= new_glyph_node
();
3357 @ A subset of the glyphs nodes represent ligatures
: characters
3358 fabricated from the interaction of two or more actual characters. The
3359 characters that generated the ligature have not been forgotten
, since
3360 they are needed for diagnostic messages
; the |lig_ptr| field points to
3361 a linked list of character nodes for all original characters that have
3362 been deleted.
(This list might be empty if the characters that
3363 generated the ligature were retained in other nodes.
)
3365 The |subtype| field of these |glyph_node|s is
1, plus
2 and
/or
1 if
3366 the original source of the ligature included implicit left and
/or
3367 right boundaries. These nodes are created by the C function |new_ligkern|.
3369 A third general type of glyphs could be called a character
, as it
3370 only appears in lists that are not yet processed by the ligaturing and
3371 kerning steps of the program.
3373 |main_control| inserts these
, and they are later converted to
3374 |subtype_normal| by |new_ligkern|.
3377 quarterword norm_min
(int h
)
3384 return
(quarterword
) h
;
3387 halfword new_char
(int f
, int c
)
3389 halfword p
; /* the new node
*/
3390 p
= new_glyph_node
();
3391 set_to_character
(p
);
3394 lang_data
(p
) = make_lang_data
(uc_hyph
, cur_lang
, left_hyphen_min
, right_hyphen_min
);
3398 @ Left and right ghost glyph nodes are the result of \.
{\\leftghost
}
3399 and \.
{\\rightghost
}, respectively. They are going to be removed by
3400 |new_ligkern|
, at the end of which they are no longer needed.
3402 @ Here are a few handy helpers used by the list output routines.
3405 scaled glyph_width
(halfword p
)
3407 scaled w
= char_width
(font
(p
), character
(p
));
3411 scaled glyph_height
(halfword p
)
3413 scaled w
= char_height
(font
(p
), character
(p
)) + y_displace
(p
);
3419 scaled glyph_depth
(halfword p
)
3421 scaled w
= char_depth
(font
(p
), character
(p
));
3422 if
(y_displace
(p
) > 0)
3423 w
= w
- y_displace
(p
);
3429 @ A |disc_node|
, which occurs only in horizontal lists
, specifies a
3430 ``dis\
-cretion\
-ary'' line break. If such a break occurs at node |p|
, the text
3431 that starts at |pre_break
(p
)| will precede the break
, the text that starts at
3432 |post_break
(p
)| will follow the break
, and text that appears in
3433 |no_break
(p
)| nodes will be ignored. For example
, an ordinary
3434 discretionary hyphen
, indicated by `\.
{\\
-}'
, yields a |disc_node| with
3435 |pre_break| pointing to a |char_node| containing a hyphen
, |post_break
=null|
,
3436 and |no_break
=null|.
3438 {TODO
: Knuth said
: All three of the discretionary texts must be lists
3439 that consist entirely of character
, kern
, box and rule nodes.
}
3441 If |subtype
(p
)=automatic_disc|
, the |ex_hyphen_penalty| will be charged for this
3442 break. Otherwise the |hyphen_penalty| will be charged. The texts will
3443 actually be substituted into the list by the line-breaking algorithm if it
3444 decides to make the break
, and the discretionary node will disappear at
3445 that time
; thus
, the output routine sees only discretionaries that were
3449 halfword new_disc
(void
)
3450 { /* creates an empty |disc_node|
*/
3451 halfword p
= new_node
(disc_node
, 0);
3452 disc_penalty
(p
) = int_par
(hyphen_penalty_code
);
3456 @ A |whatsit_node| is a wild card reserved for extensions to \TeX. The
3457 |subtype| field in its first word says what `\\
{whatsit
}' it is
, and
3458 implicitly determines the node size
(which must be
2 or more
) and the
3459 format of the remaining words. When a |whatsit_node| is encountered
3460 in a list
, special actions are invoked
; knowledgeable people who are
3461 careful not to mess up the rest of \TeX\ are able to make \TeX\ do new
3462 things by adding code at the end of the program. For example
, there
3463 might be a `\TeX nicolor' extension to specify different colors of ink
,
3464 @^extensions to \TeX@
>
3465 and the whatsit node might contain the desired parameters.
3467 The present implementation of \TeX\ treats the features associated with
3468 `\.
{\\write
}' and `\.
{\\special
}' as if they were extensions
, in order to
3469 illustrate how such routines might be coded. We shall defer further
3470 discussion of extensions until the end of this program.
3472 @ A |math_node|
, which occurs only in horizontal lists
, appears before and
3473 after mathematical formulas. The |subtype| field is |before| before the
3474 formula and |after| after it. There is a |surround| field
, which represents
3475 the amount of surrounding space inserted by \.
{\\mathsurround
}.
3478 halfword new_math
(scaled w
, int s
)
3480 halfword p
= new_node
(math_node
, s
);
3485 @ \TeX\ makes use of the fact that |hlist_node|
, |vlist_node|
,
3486 |rule_node|
, |ins_node|
, |mark_node|
, |adjust_node|
,
3487 |disc_node|
, |whatsit_node|
, and |math_node| are at the low end of the
3488 type codes
, by permitting a break at glue in a list if and only if the
3489 |type| of the previous node is less than |math_node|. Furthermore
, a
3490 node is discarded after a break if its type is |math_node| or~more.
3492 @ A |glue_node| represents glue in a list. However
, it is really only
3493 a pointer to a separate glue specification
, since \TeX\ makes use of the
3494 fact that many essentially identical nodes of glue are usually present.
3495 If |p| points to a |glue_node|
, |glue_ptr
(p
)| points to
3496 another packet of words that specify the stretch and shrink components
, etc.
3498 Glue nodes also serve to represent leaders
; the |subtype| is used to
3499 distinguish between ordinary glue
(which is called |normal|
) and the three
3500 kinds of leaders
(which are called |a_leaders|
, |c_leaders|
, and |x_leaders|
).
3501 The |leader_ptr| field points to a rule node or to a box node containing the
3502 leaders
; it is set to |null| in ordinary glue nodes.
3504 Many kinds of glue are computed from \TeX's ``skip'' parameters
, and
3505 it is helpful to know which parameter has led to a particular glue node.
3506 Therefore the |subtype| is set to indicate the source of glue
, whenever
3507 it originated as a parameter. We will be defining symbolic names for the
3508 parameter numbers later
(e.g.
, |line_skip_code
=0|
, |baseline_skip_code
=1|
,
3509 etc.
); it suffices for now to say that the |subtype| of parametric glue
3510 will be the same as the parameter number
, plus~one.
3512 @ In math formulas there are two more possibilities for the |subtype| in a
3513 glue node
: |mu_glue| denotes an \.
{\\mskip
} (where the units are scaled \.
{mu
}
3514 instead of scaled \.
{pt
}); and |cond_math_glue| denotes the `\.
{\\nonscript
}'
3515 feature that cancels the glue node immediately following if it appears
3518 @ A glue specification has a halfword reference count in its first word
,
3519 @^reference counts@
>
3520 representing |null| plus the number of glue nodes that point to it
(less one
).
3521 Note that the reference count appears in the same position as
3522 the |link| field in list nodes
; this is the field that is initialized
3523 to |null| when a node is allocated
, and it is also the field that is flagged
3524 by |empty_flag| in empty nodes.
3526 Glue specifications also contain three |scaled| fields
, for the |width|
,
3527 |stretch|
, and |shrink| dimensions. Finally
, there are two one-byte
3528 fields called |stretch_order| and |shrink_order|
; these contain the
3529 orders of infinity
(|normal|
, |sfi|
, |fil|
, |fill|
, or |filll|
)
3530 corresponding to the stretch and shrink values.
3532 @ Here is a function that returns a pointer to a copy of a glue spec.
3533 The reference count in the copy is |null|
, because there is assumed
3534 to be exactly one reference to the new specification.
3537 halfword new_spec
(halfword p
)
3539 return copy_node
(p
== null ? zero_glue
: p
);
3542 @ And here's a function that creates a glue node for a given parameter
3543 identified by its code number
; for example
,
3544 |new_param_glue
(line_skip_code
)| returns a pointer to a glue node for the
3545 current \.
{\\lineskip
}.
3548 halfword new_param_glue
(int n
)
3550 halfword p
= new_node
(glue_node
, n
+ 1);
3551 halfword q
= glue_par
(n
);
3552 width
(p
) = width
(q
);
3553 stretch
(p
) = stretch
(q
);
3554 shrink
(p
) = shrink
(q
);
3555 stretch_order
(p
) = stretch_order
(q
);
3556 shrink_order
(p
) = shrink_order
(q
);
3560 @ Glue nodes that are more or less anonymous are created by |new_glue|
,
3561 whose argument points to a glue specification.
3564 halfword new_glue
(halfword q
)
3566 halfword p
= new_node
(glue_node
, normal
);
3567 width
(p
) = width
(q
);
3568 stretch
(p
) = stretch
(q
);
3569 shrink
(p
) = shrink
(q
);
3570 stretch_order
(p
) = stretch_order
(q
);
3571 shrink_order
(p
) = shrink_order
(q
);
3575 @ Still another subroutine is needed
: This one is sort of a combination
3576 of |new_param_glue| and |new_glue|. It creates a glue node for one of
3577 the current glue parameters
, but it makes a fresh copy of the glue
3578 specification
, since that specification will probably be subject to change
,
3579 while the parameter will stay put.
3582 The global variable |temp_ptr| is set to the address of the new spec.
3586 halfword new_skip_param
(int n
)
3588 halfword p
= new_node
(glue_node
, n
+ 1);
3589 halfword q
= glue_par
(n
);
3590 width
(p
) = width
(q
);
3591 stretch
(p
) = stretch
(q
);
3592 shrink
(p
) = shrink
(q
);
3593 stretch_order
(p
) = stretch_order
(q
);
3594 shrink_order
(p
) = shrink_order
(q
);
3598 @ A |kern_node| has a |width| field to specify a
(normally negative
)
3599 amount of spacing. This spacing correction appears in horizontal lists
3600 between letters like A and V when the font designer said that it looks
3601 better to move them closer together or further apart. A kern node can
3602 also appear in a vertical list
, when its `|width|' denotes additional
3603 spacing in the vertical direction. The |subtype| is either |normal|
(for
3604 kerns inserted from font information or math mode calculations
) or |explicit|
3605 (for kerns inserted from \.
{\\kern
} and \.
{\\
/} commands
) or |acc_kern|
3606 (for kerns inserted from non-math accents
) or |mu_glue|
(for kerns
3607 inserted from \.
{\\mkern
} specifications in math formulas
).
3609 @ The |new_kern| function creates a kern node having a given width.
3612 halfword new_kern
(scaled w
)
3614 halfword p
= new_node
(kern_node
, normal
);
3619 @ A |penalty_node| specifies the penalty associated with line or page
3620 breaking
, in its |penalty| field. This field is a fullword integer
, but
3621 the full range of integer values is not used
: Any penalty |
>=10000| is
3622 treated as infinity
, and no break will be allowed for such high values.
3623 Similarly
, any penalty |
<=-10000| is treated as negative infinity
, and a
3624 break will be forced.
3626 @ Anyone who has been reading the last few sections of the program will
3627 be able to guess what comes next.
3630 halfword new_penalty
(int m
)
3632 halfword p
= new_node
(penalty_node
, 0); /* the |subtype| is not used
*/
3637 @ You might think that we have introduced enough node types by now. Well
,
3638 almost
, but there is one more
: An |unset_node| has nearly the same format
3639 as an |hlist_node| or |vlist_node|
; it is used for entries in \.
{\\halign
}
3640 or \.
{\\valign
} that are not yet in their final form
, since the box
3641 dimensions are their ``natural'' sizes before any glue adjustment has been
3642 made. The |glue_set| word is not present
; instead
, we have a |glue_stretch|
3643 field
, which contains the total stretch of order |glue_order| that is
3644 present in the hlist or vlist being boxed.
3645 Similarly
, the |shift_amount| field is replaced by a |glue_shrink| field
,
3646 containing the total shrink of order |glue_sign| that is present.
3647 The |subtype| field is called |span_count|
; an unset box typically
3648 contains the data for |qo
(span_count
)+1| columns.
3649 Unset nodes will be changed to box nodes when alignment is completed.
3651 In fact
, there are still more types coming. When we get to math formula
3652 processing we will see that a |style_node| has |type
=14|
; and a number
3653 of larger type codes will also be defined
, for use in math mode only.
3655 Warning
: If any changes are made to these data structure layouts
, such as
3656 changing any of the node sizes or even reordering the words of nodes
,
3657 the |copy_node_list| procedure and the memory initialization code
3658 below may have to be changed. Such potentially dangerous parts of the
3659 program are listed in the index under `data structure assumptions'.
3660 @
!@^data structure assumptions@
>
3661 However
, other references to the nodes are made symbolically in terms of
3662 the \.
{WEB
} macro definitions above
, so that format changes will leave
3663 \TeX's other algorithms intact.
3664 @^system dependencies@
>
3666 @ This function creates a |local_paragraph| node
3670 halfword make_local_par_node
(int mode
)
3674 halfword p
= new_node
(local_par_node
,0);
3675 local_pen_inter
(p
) = local_inter_line_penalty
;
3676 local_pen_broken
(p
) = local_broken_penalty
;
3677 if
(local_left_box
!= null
) {
3678 q
= copy_node_list
(local_left_box
);
3679 local_box_left
(p
) = q
;
3680 local_box_left_width
(p
) = width
(local_left_box
);
3682 if
(local_right_box
!= null
) {
3683 q
= copy_node_list
(local_right_box
);
3684 local_box_right
(p
) = q
;
3685 local_box_right_width
(p
) = width
(local_right_box
);
3687 local_par_dir
(p
) = par_direction
;
3688 /* callback with node passed
*/
3689 callback_id
= callback_defined
(insert_local_par_callback
);
3690 if
(callback_id
> 0) {
3691 int sfix
= lua_gettop
(Luas
);
3692 if
(!get_callback
(Luas
, callback_id
)) {
3693 lua_settop
(Luas
, sfix
);
3696 nodelist_to_lua
(Luas
, p
);
3697 lua_push_local_par_mode
(Luas
,mode
)
3698 if
(lua_pcall
(Luas
, 2, 0, 0) != 0) { /* 2 arg
, 0 result
*/
3699 char errmsg
[256]; /* temp hack ... we will have a formatted error
*/
3700 snprintf
(errmsg
, 255, "error: %s\n", lua_tostring
(Luas
, -1));
3702 lua_settop
(Luas
, sfix
);
3703 normal_error
("insert_local_par",errmsg
); /* to be done
*/
3706 lua_settop
(Luas
, sfix
);