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
/>.
24 #include
"lua/luatex-api.h"
30 #define DEBUG_OUT stdout
32 #define adjust_pre subtype
33 #define attribute
(A
) eqtb
[attribute_base
+(A
)].cint
35 #define uc_hyph int_par
(uc_hyph_code
)
36 #define cur_lang int_par
(cur_lang_code
)
37 #define left_hyphen_min int_par
(left_hyphen_min_code
)
38 #define right_hyphen_min int_par
(right_hyphen_min_code
)
40 #define text_direction int_par
(text_direction_code
)
42 #define MAX_CHAIN_SIZE
13
44 memory_word
*volatile varmem
= NULL;
47 char
*varmem_sizes
= NULL;
50 halfword var_mem_max
= 0;
53 halfword free_chain
[MAX_CHAIN_SIZE
] = { null
};
55 static int my_prealloc
= 0;
57 int fix_node_lists
= 1;
59 int free_error_seen
= 0;
60 int copy_error_seen
= 0;
62 halfword slow_get_node
(int s
); /* defined below
*/
63 int copy_error
(halfword p
); /* define below
*/
66 #define fake_node_size
2
67 #define fake_node_name
"fake"
69 #define variable_node_size
2
71 const char
*node_fields_list
[] =
72 { "attr", "width", "depth", "height", "dir", "shift",
73 "glue_order", "glue_sign", "glue_set", "head", NULL
75 const char
*node_fields_rule
[] =
76 { "attr", "width", "depth", "height", "dir", NULL };
77 const char
*node_fields_insert
[] =
78 { "attr", "cost", "depth", "height", "spec", "head", NULL };
79 const char
*node_fields_mark
[] = { "attr", "class", "mark", NULL };
80 const char
*node_fields_adjust
[] = { "attr", "head", NULL };
81 const char
*node_fields_disc
[] = { "attr", "pre", "post", "replace", "penalty", NULL };
82 const char
*node_fields_math
[] = { "attr", "surround", NULL };
83 const char
*node_fields_glue
[] = { "attr", "spec", "leader", NULL };
84 const char
*node_fields_kern
[] = { "attr", "kern", "expansion_factor", NULL };
85 const char
*node_fields_penalty
[] = { "attr", "penalty", NULL };
87 const char
*node_fields_unset
[] =
88 { "attr", "width", "depth", "height", "dir", "shrink",
89 "glue_order", "glue_sign", "stretch", "span", "head", NULL
91 const char
*node_fields_margin_kern
[] = { "attr", "width", "glyph", NULL };
93 const char
*node_fields_glyph
[] =
94 { "attr", "char", "font", "lang", "left", "right", "uchyph",
95 "components", "xoffset", "yoffset", "width", "height", "depth", "expansion_factor", NULL
97 const char
*node_fields_style
[] = { "attr", "style", NULL };
98 const char
*node_fields_choice
[] =
99 { "attr", "display", "text", "script", "scriptscript", NULL };
100 const char
*node_fields_ord
[] = { "attr", "nucleus", "sub", "sup", NULL };
101 const char
*node_fields_op
[] = { "attr", "nucleus", "sub", "sup", NULL };
102 const char
*node_fields_bin
[] = { "attr", "nucleus", "sub", "sup", NULL };
103 const char
*node_fields_rel
[] = { "attr", "nucleus", "sub", "sup", NULL };
104 const char
*node_fields_open
[] = { "attr", "nucleus", "sub", "sup", NULL };
105 const char
*node_fields_close
[] = { "attr", "nucleus", "sub", "sup", NULL };
106 const char
*node_fields_punct
[] = { "attr", "nucleus", "sub", "sup", NULL };
107 const char
*node_fields_inner
[] = { "attr", "nucleus", "sub", "sup", NULL };
108 const char
*node_fields_under
[] = { "attr", "nucleus", "sub", "sup", NULL };
109 const char
*node_fields_over
[] = { "attr", "nucleus", "sub", "sup", NULL };
110 const char
*node_fields_vcenter
[] = { "attr", "nucleus", "sub", "sup", NULL };
111 const char
*node_fields_radical
[] =
112 { "attr", "nucleus", "sub", "sup", "left", "degree", NULL };
113 const char
*node_fields_fraction
[] =
114 { "attr", "width", "num", "denom", "left", "right", NULL };
115 const char
*node_fields_accent
[] =
116 { "attr", "nucleus", "sub", "sup", "accent", "bot_accent", NULL };
117 const char
*node_fields_fence
[] = { "attr", "delim", NULL };
118 const char
*node_fields_math_char
[] = { "attr", "fam", "char", NULL };
119 const char
*node_fields_sub_box
[] = { "attr", "head", NULL };
120 const char
*node_fields_sub_mlist
[] = { "attr", "head", NULL };
121 const char
*node_fields_math_text_char
[] = { "attr", "fam", "char", NULL };
122 const char
*node_fields_delim
[] =
123 { "attr", "small_fam", "small_char", "large_fam", "large_char", NULL };
125 const char
*node_fields_inserting
[] =
126 { "height", "last_ins_ptr", "best_ins_ptr", NULL };
128 const char
*node_fields_splitup
[] = { "height", "last_ins_ptr", "best_ins_ptr",
129 "broken_ptr", "broken_ins", NULL
132 const char
*node_fields_action
[] = { "action_type", "named_id", "action_id",
133 "file", "new_window", "data", "ref_count", NULL
135 const char
*node_fields_attribute
[] = { "number", "value", NULL };
137 const char
*node_fields_glue_spec
[] = { "width", "stretch", "shrink",
138 "stretch_order", "shrink_order", "ref_count", "writable", NULL
140 const char
*node_fields_attribute_list
[] = { NULL };
142 const char
*node_fields_whatsit_open
[] =
143 { "attr", "stream", "name", "area", "ext", NULL };
144 const char
*node_fields_whatsit_write
[] = { "attr", "stream", "data", NULL };
145 const char
*node_fields_whatsit_close
[] = { "attr", "stream", NULL };
146 const char
*node_fields_whatsit_special
[] = { "attr", "data", NULL };
148 const char
*node_fields_whatsit_local_par
[] =
149 { "attr", "pen_inter", "pen_broken", "dir",
150 "box_left", "box_left_width", "box_right", "box_right_width", NULL
152 const char
*node_fields_whatsit_dir
[] =
153 { "attr", "dir", "level", "dvi_ptr", "dvi_h", NULL };
155 const char
*node_fields_whatsit_pdf_literal
[] =
156 { "attr", "mode", "data", NULL };
157 const char
*node_fields_whatsit_pdf_refobj
[] = { "attr", "objnum", NULL };
158 const char
*node_fields_whatsit_pdf_refxform
[] =
159 { "attr", "width", "depth", "height", "objnum", NULL };
160 const char
*node_fields_whatsit_pdf_refximage
[] =
161 { "attr", "width", "depth", "height", "transform", "index", NULL };
162 const char
*node_fields_whatsit_pdf_annot
[] =
163 { "attr", "width", "depth", "height", "objnum", "data", NULL };
164 const char
*node_fields_whatsit_pdf_start_link
[] =
165 { "attr", "width", "depth", "height",
166 "objnum", "link_attr", "action", NULL
168 const char
*node_fields_whatsit_pdf_end_link
[] = { "attr", NULL };
170 const char
*node_fields_whatsit_pdf_dest
[] =
171 { "attr", "width", "depth", "height",
172 "named_id", "dest_id", "dest_type", "xyz_zoom", "objnum", NULL
175 const char
*node_fields_whatsit_pdf_thread
[] =
176 { "attr", "width", "depth", "height",
177 "named_id", "thread_id", "thread_attr", NULL
180 const char
*node_fields_whatsit_pdf_start_thread
[] =
181 { "attr", "width", "depth", "height",
182 "named_id", "thread_id", "thread_attr", NULL
184 const char
*node_fields_whatsit_pdf_end_thread
[] = { "attr", NULL };
185 const char
*node_fields_whatsit_save_pos
[] = { "attr", NULL };
186 const char
*node_fields_whatsit_late_lua
[] =
187 { "attr", "reg", "data", "name", "string", NULL };
188 const char
*node_fields_whatsit_pdf_colorstack
[] =
189 { "attr", "stack", "cmd", "data", NULL };
190 const char
*node_fields_whatsit_pdf_setmatrix
[] = { "attr", "data", NULL };
191 const char
*node_fields_whatsit_pdf_save
[] = { "attr", NULL };
192 const char
*node_fields_whatsit_pdf_restore
[] = { "attr", NULL };
193 const char
*node_fields_whatsit_cancel_boundary
[] = { "attr", NULL };
194 const char
*node_fields_whatsit_user_defined
[] =
195 { "attr", "user_id", "type", "value", NULL };
197 node_info node_data
[] = {
198 {hlist_node
, box_node_size
, node_fields_list
, "hlist"},
199 {vlist_node
, box_node_size
, node_fields_list
, "vlist"},
200 {rule_node
, rule_node_size
, node_fields_rule
, "rule"},
201 {ins_node
, ins_node_size
, node_fields_insert
, "ins"},
202 {mark_node
, mark_node_size
, node_fields_mark
, "mark"},
203 {adjust_node
, adjust_node_size
, node_fields_adjust
, "adjust"},
204 {fake_node
, fake_node_size
, NULL, fake_node_name
}, /* don't touch this
! */
205 {disc_node
, disc_node_size
, node_fields_disc
, "disc"},
206 {whatsit_node
, -1, NULL, "whatsit"},
207 {math_node
, math_node_size
, node_fields_math
, "math"},
208 {glue_node
, glue_node_size
, node_fields_glue
, "glue"},
209 {kern_node
, kern_node_size
, node_fields_kern
, "kern"},
210 {penalty_node
, penalty_node_size
, node_fields_penalty
, "penalty"},
211 {unset_node
, box_node_size
, node_fields_unset
, "unset"},
212 {style_node
, style_node_size
, node_fields_style
, "style"},
213 {choice_node
, style_node_size
, node_fields_choice
, "choice"},
214 {simple_noad
, noad_size
, node_fields_ord
, "noad"},
215 {old_op_noad
, noad_size
, node_fields_op
, "op"},
216 {old_bin_noad
, noad_size
, node_fields_bin
, "bin"},
217 {old_rel_noad
, noad_size
, node_fields_rel
, "rel"},
218 {old_open_noad
, noad_size
, node_fields_open
, "open"},
219 {old_close_noad
, noad_size
, node_fields_close
, "close"},
220 {old_punct_noad
, noad_size
, node_fields_punct
, "punct"},
221 {old_inner_noad
, noad_size
, node_fields_inner
, "inner"},
222 {radical_noad
, radical_noad_size
, node_fields_radical
, "radical"},
223 {fraction_noad
, fraction_noad_size
, node_fields_fraction
, "fraction"},
224 {old_under_noad
, noad_size
, node_fields_under
, "under"},
225 {old_over_noad
, noad_size
, node_fields_over
, "over"},
226 {accent_noad
, accent_noad_size
, node_fields_accent
, "accent"},
227 {old_vcenter_noad
, noad_size
, node_fields_vcenter
, "vcenter"},
228 {fence_noad
, fence_noad_size
, node_fields_fence
, "fence"},
229 {math_char_node
, math_kernel_node_size
, node_fields_math_char
, "math_char"},
230 {sub_box_node
, math_kernel_node_size
, node_fields_sub_box
, "sub_box"},
231 {sub_mlist_node
, math_kernel_node_size
, node_fields_sub_mlist
, "sub_mlist"},
232 {math_text_char_node
, math_kernel_node_size
, node_fields_math_text_char
, "math_text_char"},
233 {delim_node
, math_shield_node_size
, node_fields_delim
, "delim"},
234 {margin_kern_node
, margin_kern_node_size
, node_fields_margin_kern
, "margin_kern"},
235 {glyph_node
, glyph_node_size
, node_fields_glyph
, "glyph"},
236 {align_record_node
, box_node_size
, NULL, "align_record"},
237 {pseudo_file_node
, pseudo_file_node_size
, NULL, "pseudo_file"},
238 {pseudo_line_node
, variable_node_size
, NULL, "pseudo_line"},
239 {inserting_node
, page_ins_node_size
, node_fields_inserting
, "page_insert"},
240 {split_up_node
, page_ins_node_size
, node_fields_splitup
, "split_insert"},
241 {expr_node
, expr_node_size
, NULL, "expr_stack"},
242 {nesting_node
, nesting_node_size
, NULL, "nested_list"},
243 {span_node
, span_node_size
, NULL, "span"},
244 {attribute_node
, attribute_node_size
, node_fields_attribute
, "attribute"},
245 {glue_spec_node
, glue_spec_size
, node_fields_glue_spec
, "glue_spec"},
246 {attribute_list_node
, attribute_node_size
, node_fields_attribute_list
, "attribute_list"},
247 {action_node
, pdf_action_size
, node_fields_action
, "action"},
248 {temp_node
, temp_node_size
, NULL, "temp"},
249 {align_stack_node
, align_stack_node_size
, NULL, "align_stack"},
250 {movement_node
, movement_node_size
, NULL, "movement_stack"},
251 {if_node
, if_node_size
, NULL, "if_stack"},
252 {unhyphenated_node
, active_node_size
, NULL, "unhyphenated"},
253 {hyphenated_node
, active_node_size
, NULL, "hyphenated"},
254 {delta_node
, delta_node_size
, NULL, "delta"},
255 {passive_node
, passive_node_size
, NULL, "passive"},
256 {shape_node
, variable_node_size
, NULL, "shape"},
260 #define last_normal_node shape_node
262 node_info whatsit_node_data
[] = {
263 {open_node
, open_node_size
, node_fields_whatsit_open
, "open"},
264 {write_node
, write_node_size
, node_fields_whatsit_write
, "write"},
265 {close_node
, close_node_size
, node_fields_whatsit_close
, "close"},
266 {special_node
, special_node_size
, node_fields_whatsit_special
, "special"},
267 {fake_node
, fake_node_size
, NULL, fake_node_name
},
268 {fake_node
, fake_node_size
, NULL, fake_node_name
},
269 {local_par_node
, local_par_size
, node_fields_whatsit_local_par
,
271 {dir_node
, dir_node_size
, node_fields_whatsit_dir
, "dir"},
272 {pdf_literal_node
, write_node_size
, node_fields_whatsit_pdf_literal
,
274 {fake_node
, fake_node_size
, NULL, fake_node_name
},
275 {pdf_refobj_node
, pdf_refobj_node_size
, node_fields_whatsit_pdf_refobj
,
277 {fake_node
, fake_node_size
, NULL, fake_node_name
},
278 {pdf_refxform_node
, pdf_refxform_node_size
,
279 node_fields_whatsit_pdf_refxform
, "pdf_refxform"},
280 {fake_node
, fake_node_size
, NULL, fake_node_name
},
281 {pdf_refximage_node
, pdf_refximage_node_size
,
282 node_fields_whatsit_pdf_refximage
, "pdf_refximage"},
283 {pdf_annot_node
, pdf_annot_node_size
, node_fields_whatsit_pdf_annot
,
285 {pdf_start_link_node
, pdf_annot_node_size
,
286 node_fields_whatsit_pdf_start_link
, "pdf_start_link"},
287 {pdf_end_link_node
, pdf_end_link_node_size
,
288 node_fields_whatsit_pdf_end_link
, "pdf_end_link"},
289 {fake_node
, fake_node_size
, NULL, fake_node_name
},
290 {pdf_dest_node
, pdf_dest_node_size
, node_fields_whatsit_pdf_dest
,
292 {pdf_thread_node
, pdf_thread_node_size
, node_fields_whatsit_pdf_thread
,
294 {pdf_start_thread_node
, pdf_thread_node_size
,
295 node_fields_whatsit_pdf_start_thread
, "pdf_start_thread"},
296 {pdf_end_thread_node
, pdf_end_thread_node_size
,
297 node_fields_whatsit_pdf_end_thread
, "pdf_end_thread"},
298 {save_pos_node
, save_pos_node_size
,
299 node_fields_whatsit_save_pos
, "save_pos"},
300 {pdf_thread_data_node
, pdf_thread_node_size
, NULL, "pdf_thread_data"},
301 {pdf_link_data_node
, pdf_annot_node_size
, NULL, "pdf_link_data"},
302 {fake_node
, fake_node_size
, NULL, fake_node_name
},
303 {fake_node
, fake_node_size
, NULL, fake_node_name
},
304 {fake_node
, fake_node_size
, NULL, fake_node_name
},
305 {fake_node
, fake_node_size
, NULL, fake_node_name
},
306 {fake_node
, fake_node_size
, NULL, fake_node_name
},
307 {fake_node
, fake_node_size
, NULL, fake_node_name
},
308 {fake_node
, fake_node_size
, NULL, fake_node_name
},
309 {fake_node
, fake_node_size
, NULL, fake_node_name
},
310 {fake_node
, fake_node_size
, NULL, fake_node_name
},
311 {late_lua_node
, late_lua_node_size
, node_fields_whatsit_late_lua
,
313 {fake_node
, fake_node_size
, NULL, fake_node_name
},
314 {fake_node
, fake_node_size
, NULL, fake_node_name
},
315 {fake_node
, fake_node_size
, NULL, fake_node_name
},
316 {pdf_colorstack_node
, pdf_colorstack_node_size
,
317 node_fields_whatsit_pdf_colorstack
, "pdf_colorstack"},
318 {pdf_setmatrix_node
, pdf_setmatrix_node_size
,
319 node_fields_whatsit_pdf_setmatrix
, "pdf_setmatrix"},
320 {pdf_save_node
, pdf_save_node_size
, node_fields_whatsit_pdf_save
,
322 {pdf_restore_node
, pdf_restore_node_size
, node_fields_whatsit_pdf_restore
,
324 {cancel_boundary_node
, cancel_boundary_size
,
325 node_fields_whatsit_cancel_boundary
, "cancel_boundary"},
326 {user_defined_node
, user_defined_node_size
,
327 node_fields_whatsit_user_defined
, "user_defined"},
331 #define last_whatsit_node user_defined_node
337 When we copy a node list
, there are several possibilities
: we do the same as a new node
,
338 we copy the entry to the table in properties
(a reference
), we do a deep copy of a table
339 in the properties
, we create a new table and give it the original one as a metatable.
340 After some experiments
(that also included timing
) with these scenarios I decided that a
341 deep copy made no sense
, nor did nilling. In the end both the shallow copy and the metatable
342 variant were both ok
, although the second ons is slower. The most important aspect to keep
343 in mind is that references to other nodes in properties no longer can be valid for that
344 copy. We could use two tables
(one unique and one shared
) or metatables but that only
347 When defining a new node
, we could already allocate a table but it is rather easy to do
348 that at the lua end e.g. using a metatable __index method. That way it is under macro
351 When deleting a node
, we could keep the slot
(e.g. setting it to false
) but it could make
352 memory consumption raise unneeded when we have temporary large node lists and after that
355 So
, in the end this is what we ended up with. For the record
, I also experimented with the
358 - copy attributes to the properties so that we hav efast access at the lua end
: in the end
359 the overhead is not compensated by speed and convenience
, in fact
, attributes are not
360 that slow when it comes to accessing them
361 - a bitset in the node but again the gain compared to attributes is neglectable and it also
362 demands a pretty string agreement over what bit represents what
, and this is unlikely to
363 succeed in the tex community
(I could use it for font handling
, which is cross package
,
364 but decided that it doesn't pay off
366 In case one wonders why properties make sense then
, well
, it is not so much speed that we
367 gain
, but more convenience
: storing all kind of
(temporary
) data in attributes is no fun and
368 this mechanism makes sure that properties are cleaned up when a node is freed. Also
, the
369 advantage of a more or less global properties table is that we stay at the lua end. An
370 alternative is to store a reference in the node itself but that is complicated by the fact
371 that the register has some limitations
(no numeric keys
) and we also don't want to mess with
376 int lua_properties_level
= 0 ; /* can be private
*/
377 int lua_properties_enabled
= 0 ;
378 int lua_properties_use_metatable
= 0 ;
380 /* We keep track of nesting so that we don't oveflow the stack
, and
, what is more important
,
381 don't keep resolving the registry index.
*/
383 #define lua_properties_push do
{ \
384 if
(lua_properties_enabled
) { \
385 lua_properties_level
= lua_properties_level
+ 1 ; \
386 if
(lua_properties_level
== 1) { \
387 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
, luaS_index
(node_properties
)); \
388 lua_gettable
(Luas
, LUA_REGISTRYINDEX
); \
393 #define lua_properties_pop do
{ \
394 if
(lua_properties_enabled
) { \
395 if
(lua_properties_level
== 1) \
397 lua_properties_level
= lua_properties_level
- 1 ; \
401 /* No setting is needed
: */
403 #define lua_properties_set
(target
) do
{ \
406 /* Resetting boils down to nilling.
*/
408 #define lua_properties_reset
(target
) do
{ \
409 if
(lua_properties_enabled
) { \
410 if
(lua_properties_level
== 0) { \
411 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
, luaS_index
(node_properties
)); \
412 lua_gettable
(Luas
, LUA_REGISTRYINDEX
); \
414 lua_rawseti
(Luas
,-2,target
); \
418 lua_rawseti
(Luas
,-2,target
); \
423 /* For a moment I considered supporting all kind of data types but in practice
424 that makes no sense. So we stick to a cheap shallow copy with as option a
425 metatable. Btw
, a deep copy would look like this
:
427 static void copy_lua_table
(lua_State
* L
, int index
) {
430 while
(lua_next
(L
, index-1
) != 0) {
431 lua_pushvalue
(L
, -2);
433 if
(lua_type
(L
,-1)==LUA_TTABLE
)
434 copy_lua_table
(L
,-1);
440 #define lua_properties_copy
(target
, source
) do
{ \
441 if
(lua_properties_enabled
) { \
442 lua_pushnumber
(Luas
,source
); \
443 lua_rawget
(Luas
,-2); \
444 if
(lua_type
(Luas
,-1)==LUA_TTABLE
) { \
445 copy_lua_table
(Luas
,-1); \
446 lua_pushnumber
(Luas
,target
); \
447 lua_insert
(Luas
,-2); \
448 lua_rawset
(Luas
,-3); \
457 /* isn't there a faster way to metatable?
*/
459 #define lua_properties_copy
(target
,source
) do
{ \
460 if
(lua_properties_enabled
) { \
461 if
(lua_properties_level
== 0) { \
462 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
, luaS_index
(node_properties
)); \
463 lua_gettable
(Luas
, LUA_REGISTRYINDEX
); \
464 lua_rawgeti
(Luas
,-1,source
); \
465 if
(lua_type
(Luas
,-1)==LUA_TTABLE
) { \
466 if
(lua_properties_use_metatable
) { \
467 lua_newtable
(Luas
); \
468 lua_insert
(Luas
,-2); \
469 lua_setfield
(Luas
,-2,"__index"); \
470 lua_newtable
(Luas
); \
471 lua_insert
(Luas
,-2); \
472 lua_setmetatable
(Luas
,-2); \
474 lua_rawseti
(Luas
,-2,target
); \
480 lua_rawgeti
(Luas
,-1,source
); \
481 if
(lua_type
(Luas
,-1)==LUA_TTABLE
) { \
482 if
(lua_properties_use_metatable
) { \
483 lua_newtable
(Luas
); \
484 lua_insert
(Luas
,-2); \
485 lua_setfield
(Luas
,-2,"__index"); \
486 lua_newtable
(Luas
); \
487 lua_insert
(Luas
,-2); \
488 lua_setmetatable
(Luas
,-2); \
490 lua_rawseti
(Luas
,-2,target
); \
498 /* Here end the property handlers.
*/
501 halfword new_node
(int i
, int j
)
505 s
= get_node_size
(i
, j
);
507 /* it should be possible to do this memset at |free_node
()|
*/
508 /* type
() and subtype
() will be set below
, and vlink
() is
509 set to null by |get_node
()|
, so we can do we clearing one
510 word less than |s|
*/
511 (void
) memset
((void
*) (varmem
+ n
+ 1), 0,
512 (sizeof
(memory_word
) * ((unsigned
) s
- 1)));
522 if
(j
== open_node
) {
523 open_name
(n
) = get_nullstr
();
524 open_area
(n
) = open_name
(n
);
525 open_ext
(n
) = open_name
(n
);
529 pre_break
(n
) = pre_break_head
(n
);
530 type
(pre_break
(n
)) = nesting_node
;
531 subtype
(pre_break
(n
)) = pre_break_head
(0);
532 post_break
(n
) = post_break_head
(n
);
533 type
(post_break
(n
)) = nesting_node
;
534 subtype
(post_break
(n
)) = post_break_head
(0);
535 no_break
(n
) = no_break_head
(n
);
536 type
(no_break
(n
)) = nesting_node
;
537 subtype
(no_break
(n
)) = no_break_head
(0);
540 depth
(n
) = null_flag
;
541 height
(n
) = null_flag
;
545 width
(n
) = null_flag
;
547 case pseudo_line_node
:
549 /* this is a trick that makes |pseudo_files| slightly slower
,
550 but the overall allocation faster then an explicit test
551 at the top of |new_node
()|.
554 free_node
(n
, variable_node_size
);
555 n
= slow_get_node
(j
);
556 (void
) memset
((void
*) (varmem
+ n
+ 1), 0,
557 (sizeof
(memory_word
) * ((unsigned
) j
- 1)));
563 /* handle synctex extension
*/
566 synctex_tag_math
(n
) = cur_input.synctex_tag_field
;
567 synctex_line_math
(n
) = line
;
570 synctex_tag_glue
(n
) = cur_input.synctex_tag_field
;
571 synctex_line_glue
(n
) = line
;
575 synctex_tag_kern
(n
) = cur_input.synctex_tag_field
;
576 synctex_line_kern
(n
) = line
;
582 synctex_tag_box
(n
) = cur_input.synctex_tag_field
;
583 synctex_line_box
(n
) = line
;
586 synctex_tag_rule
(n
) = cur_input.synctex_tag_field
;
587 synctex_line_rule
(n
) = line
;
590 /* take care of attributes
*/
591 if
(nodetype_has_attributes
(i
)) {
592 build_attribute_list
(n
);
593 /* lua_properties_set
*/
595 type
(n
) = (quarterword
) i
;
596 subtype
(n
) = (quarterword
) j
;
598 fprintf
(DEBUG_OUT
, "Alloc-ing %s node %d\n",
599 get_node_name
(type
(n
), subtype
(n
)), (int
) n
);
604 halfword raw_glyph_node
(void
)
607 n
= get_node
(glyph_node_size
);
608 (void
) memset
((void
*) (varmem
+ n
+ 1), 0,
609 (sizeof
(memory_word
) * (glyph_node_size
- 1)));
610 type
(n
) = glyph_node
;
615 halfword new_glyph_node
(void
)
618 n
= get_node
(glyph_node_size
);
619 (void
) memset
((void
*) (varmem
+ n
+ 1), 0,
620 (sizeof
(memory_word
) * (glyph_node_size
- 1)));
621 type
(n
) = glyph_node
;
623 build_attribute_list
(n
);
624 /* lua_properties_set
*/
629 @ makes a duplicate of the node list that starts at |p| and returns a
630 pointer to the new list
632 halfword do_copy_node_list
(halfword p
, halfword end
)
634 halfword q
= null
; /* previous position in new list
*/
635 halfword h
= null
; /* head of the list
*/
636 register halfword s
;
638 lua_properties_push
; /* saves stack and time
*/
649 lua_properties_pop
; /* saves stack and time
*/
653 halfword copy_node_list
(halfword p
)
655 return do_copy_node_list
(p
, null
);
658 /* There is no gain in using a temp var
:
660 #define copy_sub_list
(target
,source
) do
{ \
663 s
= copy_node_list
(l
); \
670 #define copy_sub_node
(target
,source
) do
{ \
683 #define copy_sub_list
(target
,source
) do
{ \
684 if
(source
!= null
) { \
685 s
= do_copy_node_list
(source
, null
); \
692 #define copy_sub_node
(target
,source
) do
{ \
693 if
(source
!= null
) { \
694 s
= copy_node
(source
); \
701 @ make a dupe of a single node
703 halfword copy_node
(const halfword p
)
705 halfword r
; /* current node being fabricated for new list
*/
706 register halfword s
; /* a helper variable for copying into variable mem
*/
709 r
= new_node
(temp_node
, 0);
712 i
= get_node_size
(type
(p
), subtype
(p
));
715 (void
) memcpy
((void
*) (varmem
+ r
), (void
*) (varmem
+ p
),
716 (sizeof
(memory_word
) * (unsigned
) i
));
718 /* handle synctex extension
*/
721 synctex_tag_math
(r
) = cur_input.synctex_tag_field
;
722 synctex_line_math
(r
) = line
;
725 synctex_tag_kern
(r
) = cur_input.synctex_tag_field
;
726 synctex_line_kern
(r
) = line
;
729 if
(nodetype_has_attributes
(type
(p
))) {
730 add_node_attr_ref
(node_attr
(p
));
732 lua_properties_copy
(r
,p
);
738 copy_sub_list
(lig_ptr
(r
),lig_ptr
(p
)) ;
741 add_glue_ref
(glue_ptr
(p
));
742 copy_sub_list
(leader_ptr
(r
),leader_ptr
(p
)) ;
747 copy_sub_list
(list_ptr
(r
),list_ptr
(p
)) ;
750 pre_break
(r
) = pre_break_head
(r
);
751 if
(vlink_pre_break
(p
) != null
) {
752 s
= copy_node_list
(vlink_pre_break
(p
));
753 alink
(s
) = pre_break
(r
);
754 tlink_pre_break
(r
) = tail_of_list
(s
);
755 vlink_pre_break
(r
) = s
;
757 assert
(tlink
(pre_break
(r
)) == null
);
759 post_break
(r
) = post_break_head
(r
);
760 if
(vlink_post_break
(p
) != null
) {
761 s
= copy_node_list
(vlink_post_break
(p
));
762 alink
(s
) = post_break
(r
);
763 tlink_post_break
(r
) = tail_of_list
(s
);
764 vlink_post_break
(r
) = s
;
766 assert
(tlink_post_break
(r
) == null
);
768 no_break
(r
) = no_break_head
(r
);
769 if
(vlink
(no_break
(p
)) != null
) {
770 s
= copy_node_list
(vlink_no_break
(p
));
771 alink
(s
) = no_break
(r
);
772 tlink_no_break
(r
) = tail_of_list
(s
);
773 vlink_no_break
(r
) = s
;
775 assert
(tlink_no_break
(r
) == null
);
779 add_glue_ref
(split_top_ptr
(p
));
780 copy_sub_list
(ins_ptr
(r
),ins_ptr
(p
)) ;
782 case margin_kern_node
:
783 copy_sub_node
(margin_char
(r
),margin_char
(p
));
786 add_token_ref
(mark_ptr
(p
));
789 copy_sub_list
(adjust_ptr
(r
),adjust_ptr
(p
));
792 copy_sub_list
(display_mlist
(r
),display_mlist
(p
)) ;
793 copy_sub_list
(text_mlist
(r
),text_mlist
(p
)) ;
794 copy_sub_list
(script_mlist
(r
),script_mlist
(p
)) ;
795 copy_sub_list
(script_script_mlist
(r
),script_script_mlist
(p
)) ;
800 copy_sub_list
(nucleus
(r
),nucleus
(p
)) ;
801 copy_sub_list
(subscr
(r
),subscr
(p
)) ;
802 copy_sub_list
(supscr
(r
),supscr
(p
)) ;
803 if
(type
(p
) == accent_noad
) {
804 copy_sub_list
(accent_chr
(r
),accent_chr
(p
)) ;
805 copy_sub_list
(bot_accent_chr
(r
),bot_accent_chr
(p
)) ;
806 } else if
(type
(p
) == radical_noad
) {
807 copy_sub_node
(left_delimiter
(r
),left_delimiter
(p
)) ;
808 copy_sub_list
(degree
(r
),degree
(p
)) ;
812 copy_sub_node
(delimiter
(r
),delimiter
(p
)) ;
816 copy_sub_list
(math_list
(r
),math_list
(p
)) ;
819 copy_sub_list
(numerator
(r
),numerator
(p
)) ;
820 copy_sub_list
(denominator
(r
),denominator
(p
)) ;
821 copy_sub_node
(left_delimiter
(r
),left_delimiter
(p
)) ;
822 copy_sub_node
(right_delimiter
(r
),right_delimiter
(p
)) ;
825 glue_ref_count
(r
) = null
;
828 switch
(subtype
(p
)) {
834 add_token_ref
(write_tokens
(p
));
836 case pdf_literal_node
:
837 copy_pdf_literal
(r
, p
);
839 case pdf_colorstack_node
:
840 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
841 add_token_ref
(pdf_colorstack_data
(p
));
843 case pdf_setmatrix_node
:
844 add_token_ref
(pdf_setmatrix_data
(p
));
850 add_token_ref
(pdf_annot_data
(p
));
852 case pdf_start_link_node
:
853 if
(pdf_link_attr
(r
) != null
)
854 add_token_ref
(pdf_link_attr
(r
));
855 add_action_ref
(pdf_link_action
(r
));
858 if
(pdf_dest_named_id
(p
) > 0)
859 add_token_ref
(pdf_dest_id
(p
));
861 case pdf_thread_node
:
862 case pdf_start_thread_node
:
863 if
(pdf_thread_named_id
(p
) > 0)
864 add_token_ref
(pdf_thread_id
(p
));
865 if
(pdf_thread_attr
(p
) != null
)
866 add_token_ref
(pdf_thread_attr
(p
));
868 case user_defined_node
:
869 switch
(user_node_type
(p
)) {
871 add_node_attr_ref
(user_node_value
(p
));
877 s
= copy_node_list
(user_node_value
(p
));
878 user_node_value
(r
) = s
;
881 /* |add_string_ref
(user_node_value
(p
));|
*//* if this was mpost ..
*/
884 add_token_ref
(user_node_value
(p
));
892 case math_text_char_node
:
902 fprintf
(DEBUG_OUT
, "Alloc-ing %s node %d (copy of %d)\n",
903 get_node_name
(type
(r
), subtype
(r
)), (int
) r
, (int
) p
);
909 int valid_node
(halfword p
)
911 if
(p
> my_prealloc
) {
912 if
(p
< var_mem_max
) {
914 if
(varmem_sizes
[p
] > 0)
925 static void do_free_error
(halfword p
)
928 char errstr
[255] = { 0 };
929 const char
*errhlp
[] = {
930 "When I tried to free the node mentioned in the error message, it turned",
931 "out it was not (or no longer) actually in use.",
932 "Errors such as these are often caused by Lua node list alteration,",
933 "but could also point to a bug in the executable. It should be safe to continue.",
943 if
(type
(p
) == glyph_node
) {
944 snprintf
(errstr
, 255,
945 "Attempt to double-free glyph (%c) node %d, ignored",
946 (int
) character
(p
), (int
) p
);
948 snprintf
(errstr
, 255, "Attempt to double-free %s node %d, ignored",
949 get_node_name
(type
(p
), subtype
(p
)), (int
) p
);
951 tex_error
(errstr
, errhlp
);
953 for
(r
= my_prealloc
+ 1; r
< var_mem_max
; r
++) {
956 while
(s
> my_prealloc
&& varmem_sizes[s] == 0)
961 && (r - s) < get_node_size(type(s), subtype(s))
964 if
(type
(s
) == disc_node
) {
966 " pointed to from %s node %d (vlink %d, alink %d): ",
967 get_node_name
(type
(s
), subtype
(s
)), (int
) s
,
968 (int
) vlink
(s
), (int
) alink
(s
));
969 fprintf
(stdout
, "pre_break(%d,%d,%d), ",
970 (int
) vlink_pre_break
(s
), (int
) tlink
(pre_break
(s
)),
971 (int
) alink
(pre_break
(s
)));
972 fprintf
(stdout
, "post_break(%d,%d,%d), ",
973 (int
) vlink_post_break
(s
),
974 (int
) tlink
(post_break
(s
)),
975 (int
) alink
(post_break
(s
)));
976 fprintf
(stdout
, "no_break(%d,%d,%d)",
977 (int
) vlink_no_break
(s
), (int
) tlink
(no_break
(s
)),
978 (int
) alink
(no_break
(s
)));
979 fprintf
(stdout
, "\n");
982 ||
(type
(s
) == glyph_node
&& lig_ptr(s) == p)
983 ||
(type
(s
) == vlist_node
&& list_ptr(s) == p)
984 ||
(type
(s
) == hlist_node
&& list_ptr(s) == p)
985 ||
(type
(s
) == unset_node
&& list_ptr(s) == p)
986 ||
(type
(s
) == ins_node
&& ins_ptr(s) == p)
989 " pointed to from %s node %d (vlink %d, alink %d): ",
990 get_node_name
(type
(s
), subtype
(s
)), (int
) s
,
991 (int
) vlink
(s
), (int
) alink
(s
));
992 if
(type
(s
) == glyph_node
) {
993 fprintf
(stdout
, "lig_ptr(%d)", (int
) lig_ptr
(s
));
994 } else if
(type
(s
) == vlist_node
995 || type
(s
) == hlist_node
) {
996 fprintf
(stdout
, "list_ptr(%d)", (int
) list_ptr
(s
));
998 fprintf
(stdout
, "\n");
1000 if
((type
(s
) != penalty_node
)
1001 && (type(s) != math_node)
1002 && (type(s) != kern_node)
1004 fprintf
(stdout
, " pointed to from %s node %d\n",
1005 get_node_name
(type
(s
), subtype
(s
)),
1016 static int free_error
(halfword p
)
1018 assert
(p
> my_prealloc
);
1019 assert
(p
< var_mem_max
);
1021 if
(varmem_sizes
[p
] == 0) {
1023 return
1; /* double free
*/
1031 static void do_copy_error
(halfword p
)
1033 char errstr
[255] = { 0 };
1034 const char
*errhlp
[] = {
1035 "When I tried to copy the node mentioned in the error message, it turned",
1036 "out it was not (or no longer) actually in use.",
1037 "Errors such as these are often caused by Lua node list alteration,",
1038 "but could also point to a bug in the executable. It should be safe to continue.",
1042 if
(copy_error_seen
)
1045 copy_error_seen
= 1;
1046 if
(type
(p
) == glyph_node
) {
1047 snprintf
(errstr
, 255,
1048 "Attempt to copy free glyph (%c) node %d, ignored",
1049 (int
) character
(p
), (int
) p
);
1051 snprintf
(errstr
, 255, "Attempt to copy free %s node %d, ignored",
1052 get_node_name
(type
(p
), subtype
(p
)), (int
) p
);
1054 tex_error
(errstr
, errhlp
);
1058 int copy_error
(halfword p
)
1061 assert
(p
< var_mem_max
);
1063 if
(p
> my_prealloc
&& varmem_sizes[p] == 0) {
1065 return
1; /* copy free node
*/
1071 /* No gain in a helper
:
1073 #define free_sub_list
(source
) do
{ \
1076 flush_node_list
(l
); \
1079 #define free_sub_node
(source
) do
{ \
1089 #define free_sub_list
(source
) do
{ \
1090 if
(source
!= null
) \
1091 flush_node_list
(source
); \
1094 #define free_sub_node
(source
) do
{ \
1095 if
(source
!= null
) \
1096 flush_node
(source
); \
1100 void flush_node
(halfword p
)
1103 if
(p
== null
) /* legal
, but no-op
*/
1107 fprintf
(DEBUG_OUT
, "Free-ing %s node %d\n",
1108 get_node_name
(type
(p
), subtype
(p
)), (int
) p
);
1115 free_sub_list
(lig_ptr
(p
));
1118 delete_glue_ref
(glue_ptr
(p
));
1119 free_sub_list
(leader_ptr
(p
));
1124 free_sub_list
(list_ptr
(p
));
1127 free_sub_list
(vlink
(pre_break
(p
)));
1128 free_sub_list
(vlink
(post_break
(p
)));
1129 free_sub_list
(vlink
(no_break
(p
)));
1130 // why not
: free_sub_list
(pre_break
(p
));
1131 // why not
: free_sub_list
(post_break
(p
));
1132 // why not
: free_sub_list
(no_break
(p
));
1139 case glue_spec_node
:
1140 /* this allows free-ing of lua-allocated glue specs
*/
1141 if
(valid_node
(p
)) {
1142 if
(glue_ref_count
(p
)!=null
) {
1143 decr
(glue_ref_count
(p
));
1145 free_node
(p
, get_node_size
(type
(p
), subtype
(p
)));
1151 switch
(subtype
(p
)) {
1159 case pdf_restore_node
:
1160 case cancel_boundary_node
:
1161 case pdf_refobj_node
:
1162 case pdf_refxform_node
:
1163 case pdf_refximage_node
:
1164 case pdf_end_link_node
:
1165 case pdf_end_thread_node
:
1167 case local_par_node
:
1171 delete_token_ref
(write_tokens
(p
));
1173 case pdf_literal_node
:
1174 free_pdf_literal
(p
);
1176 case pdf_colorstack_node
:
1177 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
1178 delete_token_ref
(pdf_colorstack_data
(p
));
1180 case pdf_setmatrix_node
:
1181 delete_token_ref
(pdf_setmatrix_data
(p
));
1186 case pdf_annot_node
:
1187 delete_token_ref
(pdf_annot_data
(p
));
1190 case pdf_link_data_node
:
1193 case pdf_start_link_node
:
1194 if
(pdf_link_attr
(p
) != null
)
1195 delete_token_ref
(pdf_link_attr
(p
));
1196 delete_action_ref
(pdf_link_action
(p
));
1199 if
(pdf_dest_named_id
(p
) > 0)
1200 delete_token_ref
(pdf_dest_id
(p
));
1203 case pdf_thread_data_node
:
1206 case pdf_thread_node
:
1207 case pdf_start_thread_node
:
1208 if
(pdf_thread_named_id
(p
) > 0)
1209 delete_token_ref
(pdf_thread_id
(p
));
1210 if
(pdf_thread_attr
(p
) != null
)
1211 delete_token_ref
(pdf_thread_attr
(p
));
1213 case user_defined_node
:
1214 switch
(user_node_type
(p
)) {
1216 delete_attribute_ref
(user_node_value
(p
));
1221 flush_node_list
(user_node_value
(p
));
1224 /* |delete_string_ref
(user_node_value
(p
));|
*//* if this was mpost ..
*/
1227 delete_token_ref
(user_node_value
(p
));
1231 const char
*hlp
[] = {
1232 "The type of the value in a user defined whatsit node should be one",
1233 "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),",
1234 "or 't' (tokenlist). Yours has an unknown type, and therefore I don't",
1235 "know how to free the node's value. A memory leak may result.",
1238 tex_error
("Unidentified user defined whatsit", hlp
);
1251 flush_node_list
(ins_ptr
(p
));
1252 delete_glue_ref
(split_top_ptr
(p
));
1254 case margin_kern_node
:
1255 flush_node
(margin_char
(p
));
1258 delete_token_ref
(mark_ptr
(p
));
1261 flush_node_list
(adjust_ptr
(p
));
1263 case style_node
: /* nothing to do
*/
1266 free_sub_list
(display_mlist
(p
));
1267 free_sub_list
(text_mlist
(p
));
1268 free_sub_list
(script_mlist
(p
));
1269 free_sub_list
(script_script_mlist
(p
));
1274 free_sub_list
(nucleus
(p
));
1275 free_sub_list
(subscr
(p
));
1276 free_sub_list
(supscr
(p
));
1277 if
(type
(p
) == accent_noad
) {
1278 free_sub_list
(accent_chr
(p
));
1279 free_sub_list
(bot_accent_chr
(p
));
1280 } else if
(type
(p
) == radical_noad
) {
1281 free_sub_node
(left_delimiter
(p
));
1282 free_sub_list
(degree
(p
));
1286 free_sub_list
(delimiter
(p
));
1288 case delim_node
: /* nothing to do
*/
1289 case math_char_node
:
1290 case math_text_char_node
:
1293 case sub_mlist_node
:
1294 free_sub_list
(math_list
(p
));
1297 free_sub_list
(numerator
(p
));
1298 free_sub_list
(denominator
(p
));
1299 free_sub_node
(left_delimiter
(p
));
1300 free_sub_node
(right_delimiter
(p
));
1302 case pseudo_file_node
:
1303 free_sub_list
(pseudo_lines
(p
));
1305 case pseudo_line_node
:
1307 free_node
(p
, subtype
(p
));
1310 case align_stack_node
:
1315 case unhyphenated_node
:
1316 case hyphenated_node
:
1320 case inserting_node
:
1323 case attribute_node
:
1324 case attribute_list_node
:
1328 fprintf
(stdout
, "flush_node: type is %d\n", type
(p
));
1332 if
(nodetype_has_attributes
(type
(p
))) {
1333 delete_attribute_ref
(node_attr
(p
));
1334 lua_properties_reset
(p
);
1336 free_node
(p
, get_node_size
(type
(p
), subtype
(p
)));
1341 void flush_node_list
(halfword pp
)
1342 { /* erase list of nodes starting at |p|
*/
1343 register halfword p
= pp
;
1344 free_error_seen
= 0;
1345 if
(p
== null
) /* legal
, but no-op
*/
1349 lua_properties_push
; /* saves stack and time
*/
1351 register halfword q
= vlink
(p
);
1355 lua_properties_pop
; /* saves stack and time
*/
1359 static int test_count
= 1;
1361 #define dorangetest
(a
,b
,c
) do
{ \
1362 if
(!(b
>=0 && b<c)) { \
1363 fprintf
(stdout
,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \
1364 (int
)a
, (int
)b
, (int
)c
, __LINE__
,test_count
); \
1365 confusion
("dorangetest"); \
1368 #define dotest
(a
,b
,c
) do
{ \
1370 fprintf
(stdout
,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \
1371 (int
)a
, (int
)b
, (int
)c
, __LINE__
,test_count
); \
1372 confusion
("dotest"); \
1375 #define check_action_ref
(a
) { dorangetest
(p
,a
,var_mem_max
); }
1376 #define check_glue_ref
(a
) { dorangetest
(p
,a
,var_mem_max
); }
1377 #define check_attribute_ref
(a
) { dorangetest
(p
,a
,var_mem_max
); }
1378 #define check_token_ref
(a
) assert
(1)
1380 void check_node
(halfword p
)
1385 dorangetest
(p
, lig_ptr
(p
), var_mem_max
);
1388 check_glue_ref
(glue_ptr
(p
));
1389 dorangetest
(p
, leader_ptr
(p
), var_mem_max
);
1394 case align_record_node
:
1395 dorangetest
(p
, list_ptr
(p
), var_mem_max
);
1398 dorangetest
(p
, ins_ptr
(p
), var_mem_max
);
1399 check_glue_ref
(split_top_ptr
(p
));
1402 switch
(subtype
(p
)) {
1404 check_token_ref
(write_tokens
(p
));
1406 case pdf_literal_node
:
1407 if
(pdf_literal_type
(p
) == normal
)
1408 check_token_ref
(pdf_literal_data
(p
));
1410 case pdf_colorstack_node
:
1411 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
1412 check_token_ref
(pdf_colorstack_data
(p
));
1414 case pdf_setmatrix_node
:
1415 check_token_ref
(pdf_setmatrix_data
(p
));
1418 if
(late_lua_name
(p
) > 0)
1419 check_token_ref
(late_lua_name
(p
));
1420 if
(late_lua_type
(p
) == normal
)
1421 check_token_ref
(late_lua_data
(p
));
1423 case pdf_annot_node
:
1424 check_token_ref
(pdf_annot_data
(p
));
1426 case pdf_start_link_node
:
1427 if
(pdf_link_attr
(p
) != null
)
1428 check_token_ref
(pdf_link_attr
(p
));
1429 check_action_ref
(pdf_link_action
(p
));
1432 if
(pdf_dest_named_id
(p
) > 0)
1433 check_token_ref
(pdf_dest_id
(p
));
1435 case pdf_thread_node
:
1436 case pdf_start_thread_node
:
1437 if
(pdf_thread_named_id
(p
) > 0)
1438 check_token_ref
(pdf_thread_id
(p
));
1439 if
(pdf_thread_attr
(p
) != null
)
1440 check_token_ref
(pdf_thread_attr
(p
));
1442 case user_defined_node
:
1443 switch
(user_node_type
(p
)) {
1445 check_attribute_ref
(user_node_value
(p
));
1448 check_token_ref
(user_node_value
(p
));
1451 dorangetest
(p
, user_node_value
(p
), var_mem_max
);
1457 confusion
("extuser");
1466 case pdf_restore_node
:
1467 case cancel_boundary_node
:
1468 case pdf_refobj_node
:
1469 case pdf_refxform_node
:
1470 case pdf_refximage_node
:
1471 case pdf_end_link_node
:
1472 case pdf_end_thread_node
:
1474 case local_par_node
:
1480 case margin_kern_node
:
1481 check_node
(margin_char
(p
));
1484 dorangetest
(p
, vlink
(pre_break
(p
)), var_mem_max
);
1485 dorangetest
(p
, vlink
(post_break
(p
)), var_mem_max
);
1486 dorangetest
(p
, vlink
(no_break
(p
)), var_mem_max
);
1489 dorangetest
(p
, adjust_ptr
(p
), var_mem_max
);
1491 case pseudo_file_node
:
1492 dorangetest
(p
, pseudo_lines
(p
), var_mem_max
);
1494 case pseudo_line_node
:
1498 dorangetest
(p
, display_mlist
(p
), var_mem_max
);
1499 dorangetest
(p
, text_mlist
(p
), var_mem_max
);
1500 dorangetest
(p
, script_mlist
(p
), var_mem_max
);
1501 dorangetest
(p
, script_script_mlist
(p
), var_mem_max
);
1504 dorangetest
(p
, numerator
(p
), var_mem_max
);
1505 dorangetest
(p
, denominator
(p
), var_mem_max
);
1506 dorangetest
(p
, left_delimiter
(p
), var_mem_max
);
1507 dorangetest
(p
, right_delimiter
(p
), var_mem_max
);
1510 dorangetest
(p
, nucleus
(p
), var_mem_max
);
1511 dorangetest
(p
, subscr
(p
), var_mem_max
);
1512 dorangetest
(p
, supscr
(p
), var_mem_max
);
1515 dorangetest
(p
, nucleus
(p
), var_mem_max
);
1516 dorangetest
(p
, subscr
(p
), var_mem_max
);
1517 dorangetest
(p
, supscr
(p
), var_mem_max
);
1518 dorangetest
(p
, degree
(p
), var_mem_max
);
1519 dorangetest
(p
, left_delimiter
(p
), var_mem_max
);
1522 dorangetest
(p
, nucleus
(p
), var_mem_max
);
1523 dorangetest
(p
, subscr
(p
), var_mem_max
);
1524 dorangetest
(p
, supscr
(p
), var_mem_max
);
1525 dorangetest
(p
, accent_chr
(p
), var_mem_max
);
1526 dorangetest
(p
, bot_accent_chr
(p
), var_mem_max
);
1529 dorangetest
(p
, delimiter
(p
), var_mem_max
);
1537 case attribute_list_node
:
1538 case attribute_node
:
1539 case glue_spec_node
:
1541 case align_stack_node
:
1546 case unhyphenated_node
:
1547 case hyphenated_node
:
1553 fprintf
(stdout
, "check_node: type is %d\n", type
(p
));
1558 static void check_static_node_mem
(void
)
1560 dotest
(zero_glue
, width
(zero_glue
), 0);
1561 dotest
(zero_glue
, type
(zero_glue
), glue_spec_node
);
1562 dotest
(zero_glue
, vlink
(zero_glue
), null
);
1563 dotest
(zero_glue
, stretch
(zero_glue
), 0);
1564 dotest
(zero_glue
, stretch_order
(zero_glue
), normal
);
1565 dotest
(zero_glue
, shrink
(zero_glue
), 0);
1566 dotest
(zero_glue
, shrink_order
(zero_glue
), normal
);
1568 dotest
(sfi_glue
, width
(sfi_glue
), 0);
1569 dotest
(sfi_glue
, type
(sfi_glue
), glue_spec_node
);
1570 dotest
(sfi_glue
, vlink
(sfi_glue
), null
);
1571 dotest
(sfi_glue
, stretch
(sfi_glue
), 0);
1572 dotest
(sfi_glue
, stretch_order
(sfi_glue
), sfi
);
1573 dotest
(sfi_glue
, shrink
(sfi_glue
), 0);
1574 dotest
(sfi_glue
, shrink_order
(sfi_glue
), normal
);
1576 dotest
(fil_glue
, width
(fil_glue
), 0);
1577 dotest
(fil_glue
, type
(fil_glue
), glue_spec_node
);
1578 dotest
(fil_glue
, vlink
(fil_glue
), null
);
1579 dotest
(fil_glue
, stretch
(fil_glue
), unity
);
1580 dotest
(fil_glue
, stretch_order
(fil_glue
), fil
);
1581 dotest
(fil_glue
, shrink
(fil_glue
), 0);
1582 dotest
(fil_glue
, shrink_order
(fil_glue
), normal
);
1584 dotest
(fill_glue
, width
(fill_glue
), 0);
1585 dotest
(fill_glue
, type
(fill_glue
), glue_spec_node
);
1586 dotest
(fill_glue
, vlink
(fill_glue
), null
);
1587 dotest
(fill_glue
, stretch
(fill_glue
), unity
);
1588 dotest
(fill_glue
, stretch_order
(fill_glue
), fill
);
1589 dotest
(fill_glue
, shrink
(fill_glue
), 0);
1590 dotest
(fill_glue
, shrink_order
(fill_glue
), normal
);
1592 dotest
(ss_glue
, width
(ss_glue
), 0);
1593 dotest
(ss_glue
, type
(ss_glue
), glue_spec_node
);
1594 dotest
(ss_glue
, vlink
(ss_glue
), null
);
1595 dotest
(ss_glue
, stretch
(ss_glue
), unity
);
1596 dotest
(ss_glue
, stretch_order
(ss_glue
), fil
);
1597 dotest
(ss_glue
, shrink
(ss_glue
), unity
);
1598 dotest
(ss_glue
, shrink_order
(ss_glue
), fil
);
1600 dotest
(fil_neg_glue
, width
(fil_neg_glue
), 0);
1601 dotest
(fil_neg_glue
, type
(fil_neg_glue
), glue_spec_node
);
1602 dotest
(fil_neg_glue
, vlink
(fil_neg_glue
), null
);
1603 dotest
(fil_neg_glue
, stretch
(fil_neg_glue
), -unity
);
1604 dotest
(fil_neg_glue
, stretch_order
(fil_neg_glue
), fil
);
1605 dotest
(fil_neg_glue
, shrink
(fil_neg_glue
), 0);
1606 dotest
(fil_neg_glue
, shrink_order
(fil_neg_glue
), normal
);
1610 void check_node_mem
(void
)
1613 check_static_node_mem
();
1615 for
(i
= (my_prealloc
+ 1); i
< var_mem_max
; i
++) {
1616 if
(varmem_sizes
[i
] > 0) {
1625 void fix_node_list
(halfword head
)
1640 halfword get_node
(int s
)
1642 register halfword r
;
1644 check_static_node_mem
();
1646 assert
(s
< MAX_CHAIN_SIZE
);
1650 free_chain
[s
] = vlink
(r
);
1652 varmem_sizes
[r
] = (char
) s
;
1655 var_used
+= s
; /* maintain usage statistics
*/
1658 /* this is the end of the 'inner loop'
*/
1659 return slow_get_node
(s
);
1664 static void print_free_chain
(int c
)
1666 halfword p
= free_chain
[c
];
1667 fprintf
(stdout
, "\nfree chain[%d] =\n ", c
);
1669 fprintf
(stdout
, "%d,", (int
) p
);
1672 fprintf
(stdout
, "null;\n");
1677 void free_node
(halfword p
, int s
)
1680 if
(p
<= my_prealloc
) {
1681 fprintf
(stdout
, "node %d (type %d) should not be freed!\n", (int
) p
,
1686 varmem_sizes
[p
] = 0;
1688 if
(s
< MAX_CHAIN_SIZE
) {
1689 vlink
(p
) = free_chain
[s
];
1692 /* todo ? it is perhaps possible to merge this node with an existing rover
*/
1695 while
(vlink
(rover
) != vlink
(p
)) {
1696 rover
= vlink
(rover
);
1700 var_used
-= s
; /* maintain statistics
*/
1704 static void free_node_chain
(halfword q
, int s
)
1706 register halfword p
= q
;
1707 while
(vlink
(p
) != null
) {
1709 varmem_sizes
[p
] = 0;
1716 varmem_sizes
[p
] = 0;
1718 vlink
(p
) = free_chain
[s
];
1724 void init_node_mem
(int t
)
1726 my_prealloc
= var_mem_stat_max
;
1727 assert
(whatsit_node_data
[user_defined_node
].id
== user_defined_node
);
1728 assert
(node_data
[passive_node
].id
== passive_node
);
1731 (memory_word
*) realloc
((void
*) varmem
,
1732 sizeof
(memory_word
) * (unsigned
) t
);
1733 if
(varmem
== NULL) {
1734 overflow
("node memory size", (unsigned
) var_mem_max
);
1736 memset
((void
*) (varmem
), 0, (unsigned
) t
* sizeof
(memory_word
));
1739 varmem_sizes
= (char
*) realloc
(varmem_sizes
, sizeof
(char
) * (unsigned
) t
);
1740 if
(varmem_sizes
== NULL) {
1741 overflow
("node memory size", (unsigned
) var_mem_max
);
1743 memset
((void
*) varmem_sizes
, 0, sizeof
(char
) * (unsigned
) t
);
1746 rover
= var_mem_stat_max
+ 1;
1747 vlink
(rover
) = rover
;
1748 node_size
(rover
) = (t
- rover
);
1750 /* initialize static glue specs
*/
1751 glue_ref_count
(zero_glue
) = null
+ 1;
1752 width
(zero_glue
) = 0;
1753 type
(zero_glue
) = glue_spec_node
;
1754 vlink
(zero_glue
) = null
;
1755 stretch
(zero_glue
) = 0;
1756 stretch_order
(zero_glue
) = normal
;
1757 shrink
(zero_glue
) = 0;
1758 shrink_order
(zero_glue
) = normal
;
1759 glue_ref_count
(sfi_glue
) = null
+ 1;
1760 width
(sfi_glue
) = 0;
1761 type
(sfi_glue
) = glue_spec_node
;
1762 vlink
(sfi_glue
) = null
;
1763 stretch
(sfi_glue
) = 0;
1764 stretch_order
(sfi_glue
) = sfi
;
1765 shrink
(sfi_glue
) = 0;
1766 shrink_order
(sfi_glue
) = normal
;
1767 glue_ref_count
(fil_glue
) = null
+ 1;
1768 width
(fil_glue
) = 0;
1769 type
(fil_glue
) = glue_spec_node
;
1770 vlink
(fil_glue
) = null
;
1771 stretch
(fil_glue
) = unity
;
1772 stretch_order
(fil_glue
) = fil
;
1773 shrink
(fil_glue
) = 0;
1774 shrink_order
(fil_glue
) = normal
;
1775 glue_ref_count
(fill_glue
) = null
+ 1;
1776 width
(fill_glue
) = 0;
1777 type
(fill_glue
) = glue_spec_node
;
1778 vlink
(fill_glue
) = null
;
1779 stretch
(fill_glue
) = unity
;
1780 stretch_order
(fill_glue
) = fill
;
1781 shrink
(fill_glue
) = 0;
1782 shrink_order
(fill_glue
) = normal
;
1783 glue_ref_count
(ss_glue
) = null
+ 1;
1785 type
(ss_glue
) = glue_spec_node
;
1786 vlink
(ss_glue
) = null
;
1787 stretch
(ss_glue
) = unity
;
1788 stretch_order
(ss_glue
) = fil
;
1789 shrink
(ss_glue
) = unity
;
1790 shrink_order
(ss_glue
) = fil
;
1791 glue_ref_count
(fil_neg_glue
) = null
+ 1;
1792 width
(fil_neg_glue
) = 0;
1793 type
(fil_neg_glue
) = glue_spec_node
;
1794 vlink
(fil_neg_glue
) = null
;
1795 stretch
(fil_neg_glue
) = -unity
;
1796 stretch_order
(fil_neg_glue
) = fil
;
1797 shrink
(fil_neg_glue
) = 0;
1798 shrink_order
(fil_neg_glue
) = normal
;
1799 /* initialize node list heads
*/
1800 vinfo
(page_ins_head
) = 0;
1801 type
(page_ins_head
) = temp_node
;
1802 vlink
(page_ins_head
) = null
;
1803 alink
(page_ins_head
) = null
;
1804 vinfo
(contrib_head
) = 0;
1805 type
(contrib_head
) = temp_node
;
1806 vlink
(contrib_head
) = null
;
1807 alink
(contrib_head
) = null
;
1808 vinfo
(page_head
) = 0;
1809 type
(page_head
) = temp_node
;
1810 vlink
(page_head
) = null
;
1811 alink
(page_head
) = null
;
1812 vinfo
(temp_head
) = 0;
1813 type
(temp_head
) = temp_node
;
1814 vlink
(temp_head
) = null
;
1815 alink
(temp_head
) = null
;
1816 vinfo
(hold_head
) = 0;
1817 type
(hold_head
) = temp_node
;
1818 vlink
(hold_head
) = null
;
1819 alink
(hold_head
) = null
;
1820 vinfo
(adjust_head
) = 0;
1821 type
(adjust_head
) = temp_node
;
1822 vlink
(adjust_head
) = null
;
1823 alink
(adjust_head
) = null
;
1824 vinfo
(pre_adjust_head
) = 0;
1825 type
(pre_adjust_head
) = temp_node
;
1826 vlink
(pre_adjust_head
) = null
;
1827 alink
(pre_adjust_head
) = null
;
1829 type
(active
) = unhyphenated_node
;
1830 vlink
(active
) = null
;
1831 alink
(active
) = null
;
1832 vinfo
(align_head
) = 0;
1833 type
(align_head
) = temp_node
;
1834 vlink
(align_head
) = null
;
1835 alink
(align_head
) = null
;
1836 vinfo
(end_span
) = 0;
1837 type
(end_span
) = span_node
;
1838 vlink
(end_span
) = null
;
1839 alink
(end_span
) = null
;
1840 type
(begin_point
) = glyph_node
;
1841 subtype
(begin_point
) = 0;
1842 vlink
(begin_point
) = null
;
1843 vinfo
(begin_point
+ 1) = null
;
1844 alink
(begin_point
) = null
;
1845 font
(begin_point
) = 0;
1846 character
(begin_point
) = '.'
;
1847 vinfo
(begin_point
+ 3) = 0;
1848 vlink
(begin_point
+ 3) = 0;
1849 vinfo
(begin_point
+ 4) = 0;
1850 vlink
(begin_point
+ 4) = 0;
1851 type
(end_point
) = glyph_node
;
1852 subtype
(end_point
) = 0;
1853 vlink
(end_point
) = null
;
1854 vinfo
(end_point
+ 1) = null
;
1855 alink
(end_point
) = null
;
1856 font
(end_point
) = 0;
1857 character
(end_point
) = '.'
;
1858 vinfo
(end_point
+ 3) = 0;
1859 vlink
(end_point
+ 3) = 0;
1860 vinfo
(end_point
+ 4) = 0;
1861 vlink
(end_point
+ 4) = 0;
1865 void dump_node_mem
(void
)
1867 dump_int
(var_mem_max
);
1869 dump_things
(varmem
[0], var_mem_max
);
1871 dump_things
(varmem_sizes
[0], var_mem_max
);
1873 dump_things
(free_chain
[0], MAX_CHAIN_SIZE
);
1875 dump_int
(my_prealloc
);
1878 @ it makes sense to enlarge the varmem array immediately
1880 void undump_node_mem
(void
)
1885 var_mem_max
= (x
< 100000 ?
100000 : x
);
1886 varmem
= xmallocarray
(memory_word
, (unsigned
) var_mem_max
);
1888 memset
((void
*)varmem
,0,x
*sizeof
(memory_word
));
1890 undump_things
(varmem
[0], x
);
1892 varmem_sizes
= xmallocarray
(char
, (unsigned
) var_mem_max
);
1893 memset
((void
*) varmem_sizes
, 0, (unsigned
) var_mem_max
* sizeof
(char
));
1894 undump_things
(varmem_sizes
[0], x
);
1896 undump_things
(free_chain
[0], MAX_CHAIN_SIZE
);
1897 undump_int
(var_used
);
1898 undump_int
(my_prealloc
);
1899 if
(var_mem_max
> x
) {
1900 /* todo ? it is perhaps possible to merge the new node with an existing rover
*/
1902 node_size
(x
) = (var_mem_max
- x
);
1903 while
(vlink
(rover
) != vlink
(x
)) {
1904 rover
= vlink
(rover
);
1911 void test_rovers
(char
*s
)
1915 fprintf
(stdout
, "%s: {rover=%d,size=%d,link=%d}", s
, r
, node_size
(r
),
1917 while
(vlink
(r
) != q
) {
1919 fprintf
(stdout
, ",{rover=%d,size=%d,link=%d}", r
, node_size
(r
),
1922 fprintf
(stdout
, "\n");
1925 # define test_rovers
(a
)
1929 halfword slow_get_node
(int s
)
1934 t
= node_size
(rover
);
1935 assert
(vlink
(rover
) < var_mem_max
);
1936 assert
(vlink
(rover
) != 0);
1937 test_rovers
("entry");
1939 register halfword r
;
1940 /* allocating from the bottom helps decrease page faults
*/
1943 vlink
(rover
) = vlink
(r
);
1944 node_size
(rover
) = node_size
(r
) - s
;
1945 if
(vlink
(rover
) != r
) { /* list is longer than one
*/
1947 while
(vlink
(q
) != r
) {
1954 test_rovers
("taken");
1955 assert
(vlink
(rover
) < var_mem_max
);
1957 varmem_sizes
[r
] = (char
) (s
> 127 ?
127 : s
);
1960 var_used
+= s
; /* maintain usage statistics
*/
1961 return r
; /* this is the only exit
*/
1964 /* attempt to keep the free list small
*/
1965 if
(vlink
(rover
) != rover
) {
1966 if
(t
< MAX_CHAIN_SIZE
) {
1967 halfword l
= vlink
(rover
);
1968 vlink
(rover
) = free_chain
[t
];
1969 free_chain
[t
] = rover
;
1971 while
(vlink
(l
) != free_chain
[t
]) {
1975 test_rovers
("outtake");
1979 while
(vlink
(rover
) != l
) {
1980 if
(node_size
(rover
) > s
) {
1983 rover
= vlink
(rover
);
1987 /* if we are still here
, it was apparently impossible to get a match
*/
1988 x
= (var_mem_max
>> 2) + s
;
1990 (memory_word
*) realloc
((void
*) varmem
,
1991 sizeof
(memory_word
) *
1992 (unsigned
) (var_mem_max
+ x
));
1993 if
(varmem
== NULL) {
1994 overflow
("node memory size", (unsigned
) var_mem_max
);
1996 memset
((void
*) (varmem
+ var_mem_max
), 0,
1997 (unsigned
) x
* sizeof
(memory_word
));
2001 (char
*) realloc
(varmem_sizes
,
2002 sizeof
(char
) * (unsigned
) (var_mem_max
+ x
));
2003 if
(varmem_sizes
== NULL) {
2004 overflow
("node memory size", (unsigned
) var_mem_max
);
2006 memset
((void
*) (varmem_sizes
+ var_mem_max
), 0,
2007 (unsigned
) (x
) * sizeof
(char
));
2010 /* todo ? it is perhaps possible to merge the new memory with an existing rover
*/
2011 vlink
(var_mem_max
) = rover
;
2012 node_size
(var_mem_max
) = x
;
2013 while
(vlink
(rover
) != vlink
(var_mem_max
)) {
2014 rover
= vlink
(rover
);
2016 vlink
(rover
) = var_mem_max
;
2017 rover
= var_mem_max
;
2018 test_rovers
("realloc");
2025 char
*sprint_node_mem_usage
(void
)
2032 int node_counts
[last_normal_node
+ last_whatsit_node
+ 2] = { 0 };
2034 for
(i
= (var_mem_max
- 1); i
> my_prealloc
; i--
) {
2035 if
(varmem_sizes
[i
] > 0) {
2036 if
(type
(i
) > last_normal_node
+ last_whatsit_node
) {
2037 node_counts
[last_normal_node
+ last_whatsit_node
+ 1]++;
2038 } else if
(type
(i
) == whatsit_node
) {
2039 node_counts
[(subtype
(i
) + last_normal_node
+ 1)]++;
2041 node_counts
[type
(i
)]++;
2047 for
(i
= 0; i
< last_normal_node
+ last_whatsit_node
+ 2; i
++) {
2048 if
(node_counts
[i
] > 0) {
2050 (i
> (last_normal_node
+ 1) ?
(i
- last_normal_node
- 1) : 0);
2051 snprintf
(msg
, 255, "%s%d %s", (b ?
", " : ""), (int
) node_counts
[i
],
2052 get_node_name
((i
> last_normal_node ? whatsit_node
: i
),
2054 ss
= xmalloc
((unsigned
) (strlen
(s
) + strlen
(msg
) + 1));
2069 halfword list_node_mem_usage
(void
)
2072 halfword p
= null
, q
= null
;
2074 char
*saved_varmem_sizes
= xmallocarray
(char
, (unsigned
) var_mem_max
);
2075 memcpy
(saved_varmem_sizes
, varmem_sizes
, (size_t
) var_mem_max
);
2076 for
(i
= my_prealloc
+ 1; i
< (var_mem_max
- 1); i
++) {
2077 if
(saved_varmem_sizes
[i
] > 0) {
2087 free
(saved_varmem_sizes
);
2093 void print_node_mem_stats
(void
)
2099 int free_chain_counts
[MAX_CHAIN_SIZE
] = { 0 };
2100 snprintf
(msg
, 255, " %d words of node memory still in use:",
2101 (int
) (var_used
+ my_prealloc
));
2103 s
= sprint_node_mem_usage
();
2108 tprint_nl
(" avail lists: ");
2110 for
(i
= 1; i
< MAX_CHAIN_SIZE
; i
++) {
2111 for
(j
= free_chain
[i
]; j
!= null
; j
= vlink
(j
))
2112 free_chain_counts
[i
]++;
2113 if
(free_chain_counts
[i
] > 0) {
2114 snprintf
(msg
, 255, "%s%d:%d", (b ?
"," : ""), i
,
2115 (int
) free_chain_counts
[i
]);
2120 print_nlp
(); /* newline
, if needed
*/
2123 /* this belongs in the web but i couldn't find the correct syntactic place
*/
2125 halfword new_span_node
(halfword n
, int s
, scaled w
)
2127 halfword p
= new_node
(span_node
, 0);
2137 static halfword new_attribute_node
(unsigned int i
, int v
)
2139 register halfword r
= get_node
(attribute_node_size
);
2140 type
(r
) = attribute_node
;
2141 attribute_id
(r
) = (halfword
) i
;
2142 attribute_value
(r
) = v
;
2143 /* not used but nicer in print
*/
2149 halfword copy_attribute_list
(halfword n
)
2151 halfword q
= get_node
(attribute_node_size
);
2152 register halfword p
= q
;
2153 type
(p
) = attribute_list_node
;
2154 attr_list_ref
(p
) = 0;
2157 register halfword r
= get_node
(attribute_node_size
);
2158 /* the link will be fixed automatically in the next loop
*/
2159 (void
) memcpy
((void
*) (varmem
+ r
), (void
*) (varmem
+ n
),
2160 (sizeof
(memory_word
) * attribute_node_size
));
2169 void update_attribute_cache
(void
)
2173 attr_list_cache
= get_node
(attribute_node_size
);
2174 type
(attr_list_cache
) = attribute_list_node
;
2175 attr_list_ref
(attr_list_cache
) = 0;
2176 p
= attr_list_cache
;
2177 for
(i
= 0; i
<= max_used_attr
; i
++) {
2178 register int v
= attribute
(i
);
2179 if
(v
> UNUSED_ATTRIBUTE
) {
2180 register halfword r
= new_attribute_node
((unsigned
) i
, v
);
2185 if
(vlink
(attr_list_cache
) == null
) {
2186 free_node
(attr_list_cache
, attribute_node_size
);
2187 attr_list_cache
= null
;
2193 void build_attribute_list
(halfword b
)
2195 if
(max_used_attr
>= 0) {
2196 if
(attr_list_cache
== cache_disabled|| attr_list_cache
== null
) {
2197 update_attribute_cache
();
2198 if
(attr_list_cache
== null
)
2201 attr_list_ref
(attr_list_cache
)++;
2202 node_attr
(b
) = attr_list_cache
;
2204 fprintf
(DEBUG_OUT
, "Added attrlist (%d) to node %d (count=%d)\n",
2205 node_attr
(b
), b
, attr_list_ref
(attr_list_cache
));
2212 halfword current_attribute_list
(void
)
2214 if
(max_used_attr
>= 0) {
2215 if
(attr_list_cache
== cache_disabled
) {
2216 update_attribute_cache
();
2218 return attr_list_cache
;
2225 void reassign_attribute
(halfword n
, halfword new
)
2230 /* there is nothing to assign but we need to check for an old value
*/
2232 delete_attribute_ref
(old
); /* also nulls attr field of n
*/
2233 } else if
(old
== null
) {
2234 /* nothing is assigned so we just do that now
*/
2235 assign_attribute_ref
(n
,new
);
2236 } else if
(old
!= new
) {
2237 /* something is assigned so we need to clean up and assign then
*/
2238 delete_attribute_ref
(old
);
2239 assign_attribute_ref
(n
,new
);
2241 /* else
: same value so there is no need to assign and change the refcount
*/
2242 node_attr
(n
) = new
;
2248 void delete_attribute_ref
(halfword b
)
2251 assert
(type
(b
) == attribute_list_node
);
2254 fprintf
(DEBUG_OUT
, "Removed attrlistref (%d) (count=%d)\n", b
,
2257 if
(attr_list_ref
(b
) == 0) {
2258 if
(b
== attr_list_cache
)
2259 attr_list_cache
= cache_disabled
;
2260 free_node_chain
(b
, attribute_node_size
);
2262 /* maintain sanity
*/
2263 if
(attr_list_ref
(b
) < 0)
2264 attr_list_ref
(b
) = 0;
2268 @ |p| is an attr list head
, or zero
2270 halfword do_set_attribute
(halfword p
, int i
, int val
)
2272 register halfword q
;
2274 if
(p
== null
) { /* add a new head \
& node */
2275 q
= get_node
(attribute_node_size
);
2276 type
(q
) = attribute_list_node
;
2277 attr_list_ref
(q
) = 1;
2278 p
= new_attribute_node
((unsigned
) i
, val
);
2283 assert
(vlink
(p
) != null
);
2284 while
(vlink
(p
) != null
) {
2285 int t
= attribute_id
(vlink
(p
));
2286 if
(t
== i
&& attribute_value(vlink(p)) == val)
2287 return q
; /* no need to do anything
*/
2297 if
(attribute_id
(vlink
(p
)) == i
) {
2298 attribute_value
(vlink
(p
)) = val
;
2299 } else
{ /* add a new node
*/
2300 halfword r
= new_attribute_node
((unsigned
) i
, val
);
2301 vlink
(r
) = vlink
(p
);
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 assert
(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 fprintf
(stdout
,"Node %d has an attribute list that is free already\n",(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
);
2383 int unset_attribute
(halfword n
, int i
, int val
)
2385 register halfword p
;
2389 if
(!nodetype_has_attributes
(type
(n
)))
2393 return UNUSED_ATTRIBUTE
;
2394 if
(attr_list_ref
(p
) == 0) {
2396 "Node %d has an attribute list that is free already\n",
2398 return UNUSED_ATTRIBUTE
;
2400 assert
(vlink
(p
) != null
);
2401 while
(vlink
(p
) != null
) {
2402 t
= attribute_id
(vlink
(p
));
2404 return UNUSED_ATTRIBUTE
;
2412 if
(attribute_id
(p
) != i
)
2413 return UNUSED_ATTRIBUTE
;
2414 /* if we are still here
, the attribute exists
*/
2416 if
(attr_list_ref
(p
) > 1 || p
== attr_list_cache
) {
2417 halfword q
= copy_attribute_list
(p
);
2418 if
(attr_list_ref
(p
) > 1) {
2419 delete_attribute_ref
(node_attr
(n
));
2421 attr_list_ref
(q
) = 1;
2424 p
= vlink
(node_attr
(n
));
2427 t
= attribute_value
(p
);
2428 if
(val
== UNUSED_ATTRIBUTE || t
== val
) {
2429 attribute_value
(p
) = UNUSED_ATTRIBUTE
;
2435 int has_attribute
(halfword n
, int i
, int val
)
2437 register halfword p
;
2438 if
(!nodetype_has_attributes
(type
(n
)))
2439 return UNUSED_ATTRIBUTE
;
2441 if
(p
== null || vlink
(p
) == null
)
2442 return UNUSED_ATTRIBUTE
;
2445 if
(attribute_id
(p
) == i
) {
2446 int ret
= attribute_value
(p
);
2447 if
(val
== UNUSED_ATTRIBUTE || val
== ret
)
2449 return UNUSED_ATTRIBUTE
;
2450 } else if
(attribute_id
(p
) > i
) {
2451 return UNUSED_ATTRIBUTE
;
2455 return UNUSED_ATTRIBUTE
;
2459 void print_short_node_contents
(halfword p
)
2476 if
(glue_ptr
(p
) != zero_glue
)
2483 short_display
(vlink
(pre_break
(p
)));
2484 short_display
(vlink
(post_break
(p
)));
2494 static void show_pdftex_whatsit_rule_spec
(int p
)
2497 print_rule_dimen
(height
(p
));
2499 print_rule_dimen
(depth
(p
));
2501 print_rule_dimen
(width
(p
));
2506 @ Each new type of node that appears in our data structure must be capable
2507 of being displayed
, copied
, destroyed
, and so on. The routines that we
2508 need for write-oriented whatsits are somewhat like those for mark nodes
;
2509 other extensions might
, of course
, involve more subtlety here.
2512 static void print_write_whatsit
(const char
*s
, pointer p
)
2515 if
(write_stream
(p
) < 16)
2516 print_int
(write_stream
(p
));
2517 else if
(write_stream
(p
) == 16)
2525 static void show_whatsit_node
(int p
)
2527 switch
(subtype
(p
)) {
2529 print_write_whatsit
("openout", p
);
2531 print_file_name
(open_name
(p
), open_area
(p
), open_ext
(p
));
2534 print_write_whatsit
("write", p
);
2535 print_mark
(write_tokens
(p
));
2538 print_write_whatsit
("closeout", p
);
2541 tprint_esc
("special");
2542 print_mark
(write_tokens
(p
));
2545 if
(dir_dir
(p
) < 0) {
2546 tprint_esc
("enddir");
2548 print_dir
(dir_dir
(p
) + 64);
2550 tprint_esc
("begindir");
2552 print_dir
(dir_dir
(p
));
2555 case local_par_node
:
2556 tprint_esc
("whatsit");
2559 print_current_string
();
2560 tprint_esc
("localinterlinepenalty");
2562 print_int
(local_pen_inter
(p
));
2564 print_current_string
();
2565 tprint_esc
("localbrokenpenalty");
2567 print_int
(local_pen_broken
(p
));
2569 print_current_string
();
2570 tprint_esc
("localleftbox");
2571 if
(local_box_left
(p
) == null
) {
2575 show_node_list
(local_box_left
(p
));
2579 print_current_string
();
2580 tprint_esc
("localrightbox");
2581 if
(local_box_right
(p
) == null
) {
2585 show_node_list
(local_box_right
(p
));
2590 case pdf_literal_node
:
2591 show_pdf_literal
(p
);
2593 case pdf_colorstack_node
:
2594 tprint_esc
("pdfcolorstack ");
2595 print_int
(pdf_colorstack_stack
(p
));
2596 switch
(pdf_colorstack_cmd
(p
)) {
2597 case colorstack_set
:
2600 case colorstack_push
:
2603 case colorstack_pop
:
2606 case colorstack_current
:
2610 confusion
("pdfcolorstack");
2613 if
(pdf_colorstack_cmd
(p
) <= colorstack_data
)
2614 print_mark
(pdf_colorstack_data
(p
));
2616 case pdf_setmatrix_node
:
2617 tprint_esc
("pdfsetmatrix");
2618 print_mark
(pdf_setmatrix_data
(p
));
2621 tprint_esc
("pdfsave");
2623 case pdf_restore_node
:
2624 tprint_esc
("pdfrestore");
2626 case cancel_boundary_node
:
2627 tprint_esc
("noboundary");
2632 case pdf_refobj_node
:
2633 tprint_esc
("pdfrefobj");
2634 if
(obj_obj_is_stream
(static_pdf
, pdf_obj_objnum
(p
))) {
2635 if
(obj_obj_stream_attr
(static_pdf
, pdf_obj_objnum
(p
)) != LUA_NOREF
) {
2637 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
,
2638 obj_obj_stream_attr
(static_pdf
, pdf_obj_objnum
(p
)));
2640 tprint
((const char
*) lua_tostring
(Luas
, -1));
2645 if
(obj_obj_is_file
(static_pdf
, pdf_obj_objnum
(p
)))
2647 if
(obj_obj_data
(static_pdf
, pdf_obj_objnum
(p
)) != LUA_NOREF
) {
2648 lua_rawgeti
(Luas
, LUA_REGISTRYINDEX
,
2649 obj_obj_data
(static_pdf
, pdf_obj_objnum
(p
)));
2651 tprint
((const char
*) lua_tostring
(Luas
, -1));
2655 case pdf_refxform_node
:
2656 case pdf_refximage_node
:
2657 if
(subtype
(p
) == pdf_refxform_node
)
2658 tprint_esc
("pdfrefxform");
2660 tprint_esc
("pdfrefximage");
2662 print_scaled
(height
(p
));
2664 print_scaled
(depth
(p
));
2666 print_scaled
(width
(p
));
2668 case pdf_annot_node
:
2669 tprint_esc
("pdfannot");
2670 show_pdftex_whatsit_rule_spec
(p
);
2671 print_mark
(pdf_annot_data
(p
));
2673 case pdf_start_link_node
:
2674 tprint_esc
("pdfstartlink");
2675 show_pdftex_whatsit_rule_spec
(p
);
2676 if
(pdf_link_attr
(p
) != null
) {
2678 print_mark
(pdf_link_attr
(p
));
2681 if
(pdf_action_type
(pdf_link_action
(p
)) == pdf_action_user
) {
2683 print_mark
(pdf_action_tokens
(pdf_link_action
(p
)));
2686 if
(pdf_action_file
(pdf_link_action
(p
)) != null
) {
2688 print_mark
(pdf_action_file
(pdf_link_action
(p
)));
2690 switch
(pdf_action_type
(pdf_link_action
(p
))) {
2691 case pdf_action_goto
:
2692 if
(pdf_action_named_id
(pdf_link_action
(p
)) > 0) {
2693 tprint
(" goto name");
2694 print_mark
(pdf_action_id
(pdf_link_action
(p
)));
2696 tprint
(" goto num");
2697 print_int
(pdf_action_id
(pdf_link_action
(p
)));
2700 case pdf_action_page
:
2702 print_int
(pdf_action_id
(pdf_link_action
(p
)));
2703 print_mark
(pdf_action_tokens
(pdf_link_action
(p
)));
2705 case pdf_action_thread
:
2706 if
(pdf_action_named_id
(pdf_link_action
(p
)) > 0) {
2707 tprint
(" thread name");
2708 print_mark
(pdf_action_id
(pdf_link_action
(p
)));
2710 tprint
(" thread num");
2711 print_int
(pdf_action_id
(pdf_link_action
(p
)));
2715 pdf_error
("displaying", "unknown action type");
2719 case pdf_end_link_node
:
2720 tprint_esc
("pdfendlink");
2723 tprint_esc
("pdfdest");
2724 if
(pdf_dest_named_id
(p
) > 0) {
2726 print_mark
(pdf_dest_id
(p
));
2729 print_int
(pdf_dest_id
(p
));
2732 switch
(pdf_dest_type
(p
)) {
2735 if
(pdf_dest_xyz_zoom
(p
) != null
) {
2737 print_int
(pdf_dest_xyz_zoom
(p
));
2740 case pdf_dest_fitbh
:
2743 case pdf_dest_fitbv
:
2757 show_pdftex_whatsit_rule_spec
(p
);
2767 case pdf_thread_node
:
2768 case pdf_start_thread_node
:
2769 if
(subtype
(p
) == pdf_thread_node
)
2770 tprint_esc
("pdfthread");
2772 tprint_esc
("pdfstartthread");
2774 print_rule_dimen
(height
(p
));
2776 print_rule_dimen
(depth
(p
));
2778 print_rule_dimen
(width
(p
));
2779 if
(pdf_thread_attr
(p
) != null
) {
2781 print_mark
(pdf_thread_attr
(p
));
2783 if
(pdf_thread_named_id
(p
) > 0) {
2785 print_mark
(pdf_thread_id
(p
));
2788 print_int
(pdf_thread_id
(p
));
2791 case pdf_end_thread_node
:
2792 tprint_esc
("pdfendthread");
2795 tprint_esc
("savepos");
2797 case user_defined_node
:
2798 tprint_esc
("whatsit");
2799 print_int
(user_node_id
(p
));
2801 switch
(user_node_type
(p
)) {
2807 show_node_list
(user_node_value
(p
));
2812 print(user_node_value(p));
2816 print_mark
(user_node_value
(p
));
2818 default
: /* only 'd'
*/
2819 print_int
(user_node_value
(p
));
2831 @ Now we are ready for |show_node_list| itself. This procedure has been
2832 written to be ``extra robust'' in the sense that it should not crash or get
2833 into a loop even if the data structures have been messed up by bugs in
2834 the rest of the program. You can safely call its parent routine
2835 |show_box
(p
)| for arbitrary values of |p| when you are debugging \TeX.
2836 However
, in the presence of bad data
, the procedure may
2837 fetch a |memory_word| whose variant is different from the way it was stored
;
2838 for example
, it might try to read |mem
[p
].hh| when |mem
[p
]|
2839 contains a scaled integer
, if |p| is a pointer that has been
2840 clobbered or chosen at random.
2843 @ |str_room| need not be checked
; see |show_box|
2845 @ Recursive calls on |show_node_list| therefore use the following pattern
:
2847 #define node_list_display
(A
) do
{ \
2849 show_node_list
(A
); \
2853 void show_node_list
(int p
)
2854 { /* prints a node list symbolically
*/
2855 int n
; /* the number of items already printed at this level
*/
2856 real g
; /* a glue ratio
, as a floating point number
*/
2857 if
((int
) cur_length
> depth_threshold
) {
2859 tprint
(" []"); /* indicate that there's been some truncation
*/
2865 print_current_string
(); /* display the nesting history
*/
2866 if
(int_par
(tracing_online_code
) < -2)
2869 if
(n
> breadth_max
) { /* time to stop
*/
2873 /* Display node |p|
*/
2874 if
(is_char_node
(p
)) {
2875 print_font_and_char
(p
);
2876 if
(is_ligature
(p
)) {
2877 /* Display ligature |p|
; */
2878 tprint
(" (ligature ");
2879 if
(is_leftboundary
(p
))
2881 font_in_short_display
= font
(p
);
2882 short_display
(lig_ptr
(p
));
2883 if
(is_rightboundary
(p
))
2892 /* Display box |p|
; */
2893 if
(type
(p
) == hlist_node
)
2895 else if
(type
(p
) == vlist_node
)
2898 tprint_esc
("unset");
2900 print_scaled
(height
(p
));
2902 print_scaled
(depth
(p
));
2904 print_scaled
(width
(p
));
2905 if
(type
(p
) == unset_node
) {
2906 /* Display special fields of the unset node |p|
; */
2907 if
(span_count
(p
) != min_quarterword
) {
2909 print_int
(span_count
(p
) + 1);
2910 tprint
(" columns)");
2912 if
(glue_stretch
(p
) != 0) {
2913 tprint
(", stretch ");
2914 print_glue
(glue_stretch
(p
), glue_order
(p
), NULL);
2916 if
(glue_shrink
(p
) != 0) {
2917 tprint
(", shrink ");
2918 print_glue
(glue_shrink
(p
), glue_sign
(p
), NULL);
2921 /* Display the value of |glue_set
(p
)|
*/
2922 /* The code will have to change in this place if |glue_ratio| is
2923 a structured type instead of an ordinary |real|. Note that this routine
2924 should avoid arithmetic errors even if the |glue_set| field holds an
2925 arbitrary random value. The following code assumes that a properly
2926 formed nonzero |real| number has absolute value $
2^
{20}$ or more when
2927 it is regarded as an integer
; this precaution was adequate to prevent
2928 floating point underflow on the author's computer.
2931 g
= (real
) (glue_set
(p
));
2932 if
((g
!= 0.0) && (glue_sign(p) != normal)) {
2933 tprint
(", glue set ");
2934 if
(glue_sign
(p
) == shrinking
)
2936 if
(g
> 20000.0 || g
< -20000.0) {
2941 print_glue
(20000 * unity
, glue_order
(p
), NULL);
2943 print_glue
(round
(unity
* g
), glue_order
(p
), NULL);
2947 if
(shift_amount
(p
) != 0) {
2948 tprint
(", shifted ");
2949 print_scaled
(shift_amount
(p
));
2951 tprint
(", direction ");
2952 print_dir
(box_dir
(p
));
2954 node_list_display
(list_ptr
(p
)); /* recursive call
*/
2957 /* Display rule |p|
; */
2958 tprint_esc
("rule(");
2959 print_rule_dimen
(height
(p
));
2961 print_rule_dimen
(depth
(p
));
2963 print_rule_dimen
(width
(p
));
2966 /* Display insertion |p|
; */
2967 tprint_esc
("insert");
2968 print_int
(subtype
(p
));
2969 tprint
(", natural size ");
2970 print_scaled
(height
(p
));
2972 print_spec
(split_top_ptr
(p
), NULL);
2974 print_scaled
(depth
(p
));
2975 tprint
("); float cost ");
2976 print_int
(float_cost
(p
));
2977 node_list_display
(ins_ptr
(p
)); /* recursive call
*/
2980 show_whatsit_node
(p
);
2983 /* Display glue |p|
; */
2984 if
(subtype
(p
) >= a_leaders
) {
2985 /* Display leaders |p|
; */
2987 switch
(subtype
(p
)) {
3003 print_spec
(glue_ptr
(p
), NULL);
3004 node_list_display
(leader_ptr
(p
)); /* recursive call
*/
3007 if
(subtype
(p
) != normal
) {
3009 if
((subtype
(p
) - 1) < thin_mu_skip_code
) {
3010 print_cmd_chr
(assign_glue_cmd
,
3011 glue_base
+ (subtype
(p
) - 1));
3012 } else if
(subtype
(p
) < cond_math_glue
) {
3013 print_cmd_chr
(assign_mu_glue_cmd
,
3014 glue_base
+ (subtype
(p
) - 1));
3015 } else if
(subtype
(p
) == cond_math_glue
) {
3016 tprint_esc
("nonscript");
3018 tprint_esc
("mskip");
3022 if
(subtype
(p
) != cond_math_glue
) {
3024 if
(subtype
(p
) < cond_math_glue
)
3025 print_spec
(glue_ptr
(p
), NULL);
3027 print_spec
(glue_ptr
(p
), "mu");
3031 case margin_kern_node
:
3033 print_scaled
(width
(p
));
3034 if
(subtype
(p
) == left_side
)
3035 tprint
(" (left margin)");
3037 tprint
(" (right margin)");
3040 /* Display kern |p|
; */
3041 /* An ``explicit'' kern value is indicated implicitly by an explicit space.
*/
3042 if
(subtype
(p
) != mu_glue
) {
3044 if
(subtype
(p
) != normal
)
3046 print_scaled
(width
(p
));
3047 if
(subtype
(p
) == acc_kern
)
3048 tprint
(" (for accent)");
3050 tprint_esc
("mkern");
3051 print_scaled
(width
(p
));
3056 /* Display math node |p|
; */
3058 if
(subtype
(p
) == before
)
3062 if
(width
(p
) != 0) {
3063 tprint
(", surrounded ");
3064 print_scaled
(width
(p
));
3068 /* Display penalty |p|
; */
3069 tprint_esc
("penalty ");
3070 print_int
(penalty
(p
));
3073 /* Display discretionary |p|
; */
3074 /* The |post_break| list of a discretionary node is indicated by a prefixed
3075 `\.
{\char'
174}' instead of the `\..' before the |pre_break| list.
*/
3076 tprint_esc
("discretionary");
3077 print_int
(disc_penalty
(p
));
3079 if
(vlink
(no_break
(p
)) != null
) {
3080 tprint
(" replacing ");
3081 node_list_display
(vlink
(no_break
(p
)));
3083 node_list_display
(vlink
(pre_break
(p
))); /* recursive call
*/
3085 show_node_list
(vlink
(post_break
(p
)));
3086 flush_char
(); /* recursive call
*/
3089 /* Display mark |p|
; */
3091 if
(mark_class
(p
) != 0) {
3093 print_int
(mark_class
(p
));
3095 print_mark
(mark_ptr
(p
));
3098 /* Display adjustment |p|
; */
3099 tprint_esc
("vadjust");
3100 if
(adjust_pre
(p
) != 0)
3102 node_list_display
(adjust_ptr
(p
)); /* recursive call
*/
3104 case glue_spec_node
:
3105 tprint
("<glue_spec ");
3106 print_spec
(p
, NULL);
3118 @ This routine finds the 'base' width of a horizontal box
, using the same logic
3119 that \TeX82 used for \.
{\\predisplaywidth
} */
3122 pointer actual_box_width
(pointer r
, scaled base_width
)
3124 scaled w
; /* calculated |size|
*/
3125 pointer p
; /* current node when calculating |pre_display_size|
*/
3126 pointer q
; /* glue specification when calculating |pre_display_size|
*/
3127 scaled d
; /* increment to |v|
*/
3128 scaled v
; /* |w| plus possible glue amount
*/
3130 v
= shift_amount
(r
) + base_width
;
3133 if
(is_char_node
(p
)) {
3144 case margin_kern_node
:
3154 /* We need to be careful that |w|
, |v|
, and |d| do not depend on any |glue_set|
3155 values
, since such values are subject to system-dependent rounding.
3156 System-dependent numbers are not allowed to infiltrate parameters like
3157 |pre_display_size|
, since \TeX82 is supposed to make the same decisions on all
3162 if
(glue_sign
(r
) == stretching
) {
3163 if
((glue_order
(r
) == stretch_order
(q
))
3164 && (stretch(q) != 0))
3166 } else if
(glue_sign
(r
) == shrinking
) {
3167 if
((glue_order
(r
) == shrink_order
(q
))
3168 && (shrink(q) != 0))
3171 if
(subtype
(p
) >= a_leaders
)
3175 if
((subtype
(p
) == pdf_refxform_node
)
3176 ||
(subtype
(p
) == pdf_refximage_node
))
3189 if
(v
< max_dimen
) {
3204 halfword tail_of_list
(halfword p
)
3207 while
(vlink
(q
) != null
)
3213 @ |delete_glue_ref| is called when a pointer to a glue
3214 specification is being withdrawn.
3217 #define fast_delete_glue_ref
(A
) do
{ \
3218 if
(glue_ref_count
(A
)==null
) { \
3221 decr
(glue_ref_count
(A
)); \
3225 void delete_glue_ref
(halfword p
)
3226 { /* |p| points to a glue specification
*/
3227 assert
(type
(p
) == glue_spec_node
);
3228 fast_delete_glue_ref
(p
);
3233 halfword temp_ptr
; /* a pointer variable for occasional emergency use
*/
3236 @ Attribute lists need two extra globals to increase processing efficiency.
3237 |max_used_attr| limits the test loop that checks for set attributes
, and
3238 |attr_list_cache| contains a pointer to an already created attribute list. It is
3239 set to the special value |cache_disabled| when the current value can no longer be
3240 trusted
: after an assignment to an attribute register
, and after a group has
3244 int max_used_attr
; /* maximum assigned attribute id
*/
3245 halfword attr_list_cache
;
3247 @ From the computer's standpoint
, \TeX's chief mission is to create
3248 horizontal and vertical lists. We shall now investigate how the elements
3249 of these lists are represented internally as nodes in the dynamic memory.
3251 A horizontal or vertical list is linked together by |link| fields in
3252 the first word of each node. Individual nodes represent boxes
, glue
,
3253 penalties
, or special things like discretionary hyphens
; because of this
3254 variety
, some nodes are longer than others
, and we must distinguish different
3255 kinds of nodes. We do this by putting a `|type|' field in the first word
,
3256 together with the link and an optional `|subtype|'.
3259 @ Character nodes appear only in horizontal lists
, never in vertical lists.
3261 An |hlist_node| stands for a box that was made from a horizontal list.
3262 Each |hlist_node| is seven words long
, and contains the following fields
3263 (in addition to the mandatory |type| and |link|
, which we shall not
3264 mention explicitly when discussing the other node types
): The |height| and
3265 |width| and |depth| are scaled integers denoting the dimensions of the
3266 box. There is also a |shift_amount| field
, a scaled integer indicating
3267 how much this box should be lowered
(if it appears in a horizontal list
),
3268 or how much it should be moved to the right
(if it appears in a vertical
3269 list
). There is a |list_ptr| field
, which points to the beginning of the
3270 list from which this box was fabricated
; if |list_ptr| is |null|
, the box
3271 is empty. Finally
, there are three fields that represent the setting of
3272 the glue
: |glue_set
(p
)| is a word of type |glue_ratio| that represents
3273 the proportionality constant for glue setting
; |glue_sign
(p
)| is
3274 |stretching| or |shrinking| or |normal| depending on whether or not the
3275 glue should stretch or shrink or remain rigid
; and |glue_order
(p
)|
3276 specifies the order of infinity to which glue setting applies
(|normal|
,
3277 |sfi|
, |fil|
, |fill|
, or |filll|
). The |subtype| field is not used.
3279 @ The |new_null_box| function returns a pointer to an |hlist_node| in
3280 which all subfields have the values corresponding to `\.
{\\hbox\
{\
}}'.
3281 The |subtype| field is set to |min_quarterword|
, since that's the desired
3282 |span_count| value if this |hlist_node| is changed to an |unset_node|.
3285 halfword new_null_box
(void
)
3286 { /* creates a new box node
*/
3287 halfword p
; /* the new node
*/
3288 p
= new_node
(hlist_node
, min_quarterword
);
3289 box_dir
(p
) = text_direction
;
3294 @ A |vlist_node| is like an |hlist_node| in all respects except that it
3295 contains a vertical list.
3298 @ A |rule_node| stands for a solid black rectangle
; it has |width|
,
3299 |depth|
, and |height| fields just as in an |hlist_node|. However
, if
3300 any of these dimensions is $
-2^
{30}$
, the actual value will be determined
3301 by running the rule up to the boundary of the innermost enclosing box.
3302 This is called a ``running dimension.'' The |width| is never running in
3303 an hlist
; the |height| and |depth| are never running in a~vlist.
3305 @ A new rule node is delivered by the |new_rule| function. It
3306 makes all the dimensions ``running
,'' so you have to change the
3307 ones that are not allowed to run.
3310 halfword new_rule
(void
)
3312 halfword p
; /* the new node
*/
3313 p
= new_node
(rule_node
, 0); /* the |subtype| is not used
*/
3318 @ Insertions are represented by |ins_node| records
, where the |subtype|
3319 indicates the corresponding box number. For example
, `\.
{\\insert
250}'
3320 leads to an |ins_node| whose |subtype| is |
250+min_quarterword|.
3321 The |height| field of an |ins_node| is slightly misnamed
; it actually holds
3322 the natural height plus depth of the vertical list being inserted.
3323 The |depth| field holds the |split_max_depth| to be used in case this
3324 insertion is split
, and the |split_top_ptr| points to the corresponding
3325 |split_top_skip|. The |float_cost| field holds the |floating_penalty| that
3326 will be used if this insertion floats to a subsequent page after a
3327 split insertion of the same class. There is one more field
, the
3328 |ins_ptr|
, which points to the beginning of the vlist for the insertion.
3330 @ A |mark_node| has a |mark_ptr| field that points to the reference count
3331 of a token list that contains the user's \.
{\\mark
} text.
3332 In addition there is a |mark_class| field that contains the mark class.
3334 @ An |adjust_node|
, which occurs only in horizontal lists
,
3335 specifies material that will be moved out into the surrounding
3336 vertical list
; i.e.
, it is used to implement \TeX's `\.
{\\vadjust
}'
3337 operation. The |adjust_ptr| field points to the vlist containing this
3340 @ A |glyph_node|
, which occurs only in horizontal lists
, specifies a
3341 glyph in a particular font
, along with its attribute list. Older
3342 versions of \TeX\ could use token memory for characters
, because the
3343 font
,char combination would fit in a single word
(both values were
3344 required to be strictly less than $
2^
{16}$
). In LuaTeX
, room is
3345 needed for characters that are larger than that
, as well as a pointer
3346 to a potential attribute list
, and the two displacement values.
3348 In turn
, that made the node so large that it made sense to merge
3349 ligature glyphs as well
, as that requires only one extra pointer. A
3350 few extra classes of glyph nodes will be introduced later. The
3351 unification of all those types makes it easier to manipulate lists of
3352 glyphs. The subtype differentiates various glyph kinds.
3354 First
, here is a function that returns a pointer to a glyph node for a given
3355 glyph in a given font. If that glyph doesn't exist
, |null| is returned
3356 instead. Nodes of this subtype are directly created only for accents
3357 and their base
(through |make_accent|
), and math nucleus items
(in the
3358 conversion from |mlist| to |hlist|
).
3361 halfword new_glyph
(int f
, int c
)
3363 halfword p
= null
; /* the new node
*/
3364 if
((f
== 0) ||
(char_exists
(f
, c
))) {
3365 p
= new_glyph_node
();
3374 @ A subset of the glyphs nodes represent ligatures
: characters
3375 fabricated from the interaction of two or more actual characters. The
3376 characters that generated the ligature have not been forgotten
, since
3377 they are needed for diagnostic messages
; the |lig_ptr| field points to
3378 a linked list of character nodes for all original characters that have
3379 been deleted.
(This list might be empty if the characters that
3380 generated the ligature were retained in other nodes.
)
3382 The |subtype| field of these |glyph_node|s is
1, plus
2 and
/or
1 if
3383 the original source of the ligature included implicit left and
/or
3384 right boundaries. These nodes are created by the C function |new_ligkern|.
3386 A third general type of glyphs could be called a character
, as it
3387 only appears in lists that are not yet processed by the ligaturing and
3388 kerning steps of the program.
3390 |main_control| inserts these
, and they are later converted to
3391 |subtype_normal| by |new_ligkern|.
3394 quarterword norm_min
(int h
)
3401 return
(quarterword
) h
;
3404 halfword new_char
(int f
, int c
)
3406 halfword p
; /* the new node
*/
3407 p
= new_glyph_node
();
3408 set_to_character
(p
);
3412 make_lang_data
(uc_hyph
, cur_lang
, left_hyphen_min
, right_hyphen_min
);
3417 @ Left and right ghost glyph nodes are the result of \.
{\\leftghost
}
3418 and \.
{\\rightghost
}, respectively. They are going to be removed by
3419 |new_ligkern|
, at the end of which they are no longer needed.
3421 @ Here are a few handy helpers used by the list output routines.
3424 scaled glyph_width
(halfword p
)
3427 w
= char_width
(font
(p
), character
(p
));
3431 scaled glyph_height
(halfword p
)
3434 w
= char_height
(font
(p
), character
(p
)) + y_displace
(p
);
3440 scaled glyph_depth
(halfword p
)
3443 w
= char_depth
(font
(p
), character
(p
));
3444 if
(y_displace
(p
) > 0)
3445 w
= w
- y_displace
(p
);
3452 @ A |disc_node|
, which occurs only in horizontal lists
, specifies a
3453 ``dis\
-cretion\
-ary'' line break. If such a break occurs at node |p|
, the text
3454 that starts at |pre_break
(p
)| will precede the break
, the text that starts at
3455 |post_break
(p
)| will follow the break
, and text that appears in
3456 |no_break
(p
)| nodes will be ignored. For example
, an ordinary
3457 discretionary hyphen
, indicated by `\.
{\\
-}'
, yields a |disc_node| with
3458 |pre_break| pointing to a |char_node| containing a hyphen
, |post_break
=null|
,
3459 and |no_break
=null|.
3461 {TODO
: Knuth said
: All three of the discretionary texts must be lists
3462 that consist entirely of character
, kern
, box and rule nodes.
}
3464 If |subtype
(p
)=automatic_disc|
, the |ex_hyphen_penalty| will be charged for this
3465 break. Otherwise the |hyphen_penalty| will be charged. The texts will
3466 actually be substituted into the list by the line-breaking algorithm if it
3467 decides to make the break
, and the discretionary node will disappear at
3468 that time
; thus
, the output routine sees only discretionaries that were
3472 halfword new_disc
(void
)
3473 { /* creates an empty |disc_node|
*/
3474 halfword p
; /* the new node
*/
3475 p
= new_node
(disc_node
, 0);
3476 disc_penalty
(p
) = int_par
(hyphen_penalty_code
);
3480 @ A |whatsit_node| is a wild card reserved for extensions to \TeX. The
3481 |subtype| field in its first word says what `\\
{whatsit
}' it is
, and
3482 implicitly determines the node size
(which must be
2 or more
) and the
3483 format of the remaining words. When a |whatsit_node| is encountered
3484 in a list
, special actions are invoked
; knowledgeable people who are
3485 careful not to mess up the rest of \TeX\ are able to make \TeX\ do new
3486 things by adding code at the end of the program. For example
, there
3487 might be a `\TeX nicolor' extension to specify different colors of ink
,
3488 @^extensions to \TeX@
>
3489 and the whatsit node might contain the desired parameters.
3491 The present implementation of \TeX\ treats the features associated with
3492 `\.
{\\write
}' and `\.
{\\special
}' as if they were extensions
, in order to
3493 illustrate how such routines might be coded. We shall defer further
3494 discussion of extensions until the end of this program.
3496 @ A |math_node|
, which occurs only in horizontal lists
, appears before and
3497 after mathematical formulas. The |subtype| field is |before| before the
3498 formula and |after| after it. There is a |surround| field
, which represents
3499 the amount of surrounding space inserted by \.
{\\mathsurround
}.
3502 halfword new_math
(scaled w
, int s
)
3504 halfword p
; /* the new node
*/
3505 p
= new_node
(math_node
, s
);
3510 @ \TeX\ makes use of the fact that |hlist_node|
, |vlist_node|
,
3511 |rule_node|
, |ins_node|
, |mark_node|
, |adjust_node|
,
3512 |disc_node|
, |whatsit_node|
, and |math_node| are at the low end of the
3513 type codes
, by permitting a break at glue in a list if and only if the
3514 |type| of the previous node is less than |math_node|. Furthermore
, a
3515 node is discarded after a break if its type is |math_node| or~more.
3517 @ A |glue_node| represents glue in a list. However
, it is really only
3518 a pointer to a separate glue specification
, since \TeX\ makes use of the
3519 fact that many essentially identical nodes of glue are usually present.
3520 If |p| points to a |glue_node|
, |glue_ptr
(p
)| points to
3521 another packet of words that specify the stretch and shrink components
, etc.
3523 Glue nodes also serve to represent leaders
; the |subtype| is used to
3524 distinguish between ordinary glue
(which is called |normal|
) and the three
3525 kinds of leaders
(which are called |a_leaders|
, |c_leaders|
, and |x_leaders|
).
3526 The |leader_ptr| field points to a rule node or to a box node containing the
3527 leaders
; it is set to |null| in ordinary glue nodes.
3529 Many kinds of glue are computed from \TeX's ``skip'' parameters
, and
3530 it is helpful to know which parameter has led to a particular glue node.
3531 Therefore the |subtype| is set to indicate the source of glue
, whenever
3532 it originated as a parameter. We will be defining symbolic names for the
3533 parameter numbers later
(e.g.
, |line_skip_code
=0|
, |baseline_skip_code
=1|
,
3534 etc.
); it suffices for now to say that the |subtype| of parametric glue
3535 will be the same as the parameter number
, plus~one.
3537 @ In math formulas there are two more possibilities for the |subtype| in a
3538 glue node
: |mu_glue| denotes an \.
{\\mskip
} (where the units are scaled \.
{mu
}
3539 instead of scaled \.
{pt
}); and |cond_math_glue| denotes the `\.
{\\nonscript
}'
3540 feature that cancels the glue node immediately following if it appears
3543 @ A glue specification has a halfword reference count in its first word
,
3544 @^reference counts@
>
3545 representing |null| plus the number of glue nodes that point to it
(less one
).
3546 Note that the reference count appears in the same position as
3547 the |link| field in list nodes
; this is the field that is initialized
3548 to |null| when a node is allocated
, and it is also the field that is flagged
3549 by |empty_flag| in empty nodes.
3551 Glue specifications also contain three |scaled| fields
, for the |width|
,
3552 |stretch|
, and |shrink| dimensions. Finally
, there are two one-byte
3553 fields called |stretch_order| and |shrink_order|
; these contain the
3554 orders of infinity
(|normal|
, |sfi|
, |fil|
, |fill|
, or |filll|
)
3555 corresponding to the stretch and shrink values.
3557 @ Here is a function that returns a pointer to a copy of a glue spec.
3558 The reference count in the copy is |null|
, because there is assumed
3559 to be exactly one reference to the new specification.
3562 halfword new_spec
(halfword p
)
3563 { /* duplicates a glue specification
*/
3564 halfword q
; /* the new spec
*/
3566 glue_ref_count
(q
) = null
;
3570 @ And here's a function that creates a glue node for a given parameter
3571 identified by its code number
; for example
,
3572 |new_param_glue
(line_skip_code
)| returns a pointer to a glue node for the
3573 current \.
{\\lineskip
}.
3576 halfword new_param_glue
(int n
)
3578 halfword p
; /* the new node
*/
3579 halfword q
; /* the glue specification
*/
3580 p
= new_node
(glue_node
, n
+ 1);
3583 incr
(glue_ref_count
(q
));
3587 @ Glue nodes that are more or less anonymous are created by |new_glue|
,
3588 whose argument points to a glue specification.
3591 halfword new_glue
(halfword q
)
3593 halfword p
; /* the new node
*/
3594 p
= new_node
(glue_node
, normal
);
3596 incr
(glue_ref_count
(q
));
3600 @ Still another subroutine is needed
: This one is sort of a combination
3601 of |new_param_glue| and |new_glue|. It creates a glue node for one of
3602 the current glue parameters
, but it makes a fresh copy of the glue
3603 specification
, since that specification will probably be subject to change
,
3604 while the parameter will stay put. The global variable |temp_ptr| is
3605 set to the address of the new spec.
3608 halfword new_skip_param
(int n
)
3610 halfword p
; /* the new node
*/
3611 temp_ptr
= new_spec
(glue_par
(n
));
3612 p
= new_glue
(temp_ptr
);
3613 glue_ref_count
(temp_ptr
) = null
;
3614 subtype
(p
) = (quarterword
) (n
+ 1);
3618 @ A |kern_node| has a |width| field to specify a
(normally negative
)
3619 amount of spacing. This spacing correction appears in horizontal lists
3620 between letters like A and V when the font designer said that it looks
3621 better to move them closer together or further apart. A kern node can
3622 also appear in a vertical list
, when its `|width|' denotes additional
3623 spacing in the vertical direction. The |subtype| is either |normal|
(for
3624 kerns inserted from font information or math mode calculations
) or |explicit|
3625 (for kerns inserted from \.
{\\kern
} and \.
{\\
/} commands
) or |acc_kern|
3626 (for kerns inserted from non-math accents
) or |mu_glue|
(for kerns
3627 inserted from \.
{\\mkern
} specifications in math formulas
).
3629 @ The |new_kern| function creates a kern node having a given width.
3632 halfword new_kern
(scaled w
)
3634 halfword p
; /* the new node
*/
3635 p
= new_node
(kern_node
, normal
);
3640 @ A |penalty_node| specifies the penalty associated with line or page
3641 breaking
, in its |penalty| field. This field is a fullword integer
, but
3642 the full range of integer values is not used
: Any penalty |
>=10000| is
3643 treated as infinity
, and no break will be allowed for such high values.
3644 Similarly
, any penalty |
<=-10000| is treated as negative infinity
, and a
3645 break will be forced.
3647 @ Anyone who has been reading the last few sections of the program will
3648 be able to guess what comes next.
3651 halfword new_penalty
(int m
)
3653 halfword p
; /* the new node
*/
3654 p
= new_node
(penalty_node
, 0); /* the |subtype| is not used
*/
3659 @ You might think that we have introduced enough node types by now. Well
,
3660 almost
, but there is one more
: An |unset_node| has nearly the same format
3661 as an |hlist_node| or |vlist_node|
; it is used for entries in \.
{\\halign
}
3662 or \.
{\\valign
} that are not yet in their final form
, since the box
3663 dimensions are their ``natural'' sizes before any glue adjustment has been
3664 made. The |glue_set| word is not present
; instead
, we have a |glue_stretch|
3665 field
, which contains the total stretch of order |glue_order| that is
3666 present in the hlist or vlist being boxed.
3667 Similarly
, the |shift_amount| field is replaced by a |glue_shrink| field
,
3668 containing the total shrink of order |glue_sign| that is present.
3669 The |subtype| field is called |span_count|
; an unset box typically
3670 contains the data for |qo
(span_count
)+1| columns.
3671 Unset nodes will be changed to box nodes when alignment is completed.
3673 In fact
, there are still more types coming. When we get to math formula
3674 processing we will see that a |style_node| has |type
=14|
; and a number
3675 of larger type codes will also be defined
, for use in math mode only.
3677 Warning
: If any changes are made to these data structure layouts
, such as
3678 changing any of the node sizes or even reordering the words of nodes
,
3679 the |copy_node_list| procedure and the memory initialization code
3680 below may have to be changed. Such potentially dangerous parts of the
3681 program are listed in the index under `data structure assumptions'.
3682 @
!@^data structure assumptions@
>
3683 However
, other references to the nodes are made symbolically in terms of
3684 the \.
{WEB
} macro definitions above
, so that format changes will leave
3685 \TeX's other algorithms intact.
3686 @^system dependencies@
>