§1299  TEX82  PART 50: DUMPING AND UNDUMPING THE TABLES 455

1299. Dumping and undumping the tables. After has seen a collection of fonts and macros, it can write all the necessary information on an auxiliary file so that production versions of TEX are able to initialize their memory at high speed. The present section of the program takes care of such output and input. We shall consider simultaneously the processes of storing and restoring, so that the inverse relation between them is clear.

The global variable format ident is a string that is printed right after the banner line when TEX is ready to start. For this string says simply ‘ ’; for other versions of TEX it says, for example, ‘ ’, showing the year, month, and day that the format file was created. We have format ident = 0 before TEX’s tables are loaded. ⟨ Global variables 13 ⟩ +≡ format ident : str number ;

1300. ⟨ Set initial values of key variables 21 ⟩ +≡ format ident ← 0;

1301. ⟨ Initialize table entries (done by only) 164 ⟩ +≡ format ident ← ␣ ;

1302. ⟨ Declare action procedures for use by main control 1043 ⟩ +≡ init procedure store fmt file; label found1 , found2 , done1 , done2 ; var j, k, l: integer ; { all-purpose indices }

p, q: pointer ; { all-purpose pointers } x: integer ; { something to dump } w: four quarters ; { four ASCII codes }

456  PART 50: DUMPING AND UNDUMPING THE TABLES  TEX82 §1303

begin ⟨ If dumping is not allowed, abort 1304 ⟩; ⟨ Create the format ident , open the format file, and inform the user that dumping has begun 1328 ⟩; ⟨ Dump constants for consistency check 1307 ⟩; ⟨ Dump the string pool 1309 ⟩; ⟨ Dump the dynamic memory 1311 ⟩; ⟨ Dump the table of equivalents 1313 ⟩; ⟨ Dump the font information 1320 ⟩; ⟨ Dump the hyphenation tables 1324 ⟩; ⟨ Dump a couple more things and the closing check word 1326 ⟩; ⟨ Close the format file 1329 ⟩; end; tini 1303. Corresponding to the procedure that dumps a format file, we have a function that reads one in. The function returns false if the dumped format is incompatible with the present TEX table sizes, etc.

define bad fmt = 6666 { go here if the format file is unacceptable } define too small (#) ≡

begin wake up terminal ; wterm ln(´ ␣ ␣ ␣ ␣´ , #); goto bad fmt ; end

⟨ Declare the function called open fmt file 524 ⟩ function load fmt file: boolean;

label bad fmt , exit ; var j, k: integer ; { all-purpose indices }

p, q: pointer ; { all-purpose pointers }

x: integer ; { something undumped }

w: four quarters ; { four ASCII codes } begin ⟨ Undump constants for consistency check 1308 ⟩; ⟨ Undump the string pool 1310 ⟩; ⟨ Undump the dynamic memory 1312 ⟩; ⟨ Undump the table of equivalents 1314 ⟩; ⟨ Undump the font information 1321 ⟩; ⟨ Undump the hyphenation tables 1325 ⟩; ⟨ Undump a couple more things and the closing check word 1327 ⟩; load fmt file ← true; return; { it worked! }

bad fmt : wake up terminal ; wterm ln(´ ␣ ␣ ␣ ␣ ´ ´ ␣ ´ );

load fmt file ← false; exit : end;

1304. The user is not allowed to dump a format file unless save ptr = 0. This condition implies that cur level = level one, hence the xeq level array is constant and it need not be dumped. ⟨ If dumping is not allowed, abort 1304 ⟩ ≡

if save ptr ≠ 0 then begin print err ( ␣ ´ ␣ ␣ ␣ ␣ ); help1 ( ` ´ ␣ ␣ ␣ ); succumb; end

This code is used in section 1302.

1305. Format files consist of memory word items, and we use the following macros to dump words of different types:

define dump wd (#) ≡

begin fmt file↑ ← #; put (fmt file); end

define dump int (#) ≡

begin fmt file↑.int ← #; put (fmt file); end

define dump hh(#) ≡

begin fmt file↑.hh ← #; put (fmt file); end

define dump qqqq (#) ≡

begin fmt file↑.qqqq ← #; put (fmt file); end

⟨ Global variables 13 ⟩ +≡ fmt file: word file; { for input or output of format information } 1306. The inverse macros are slightly more complicated, since we need to check the range of the values we are reading in. We say ‘undump(a)(b)(x)’ to read an integer value x that is supposed to be in the range a ≤ x ≤ b.

define undump wd (#) ≡

begin get (fmt file); # ← fmt file↑; end

define undump int (#) ≡

begin get (fmt file); # ← fmt file↑.int ; end

define undump hh(#) ≡

begin get (fmt file); # ← fmt file↑.hh; end

define undump qqqq (#) ≡

begin get (fmt file); # ← fmt file↑.qqqq ; end

define undump end end (#) ≡ # ← x; end define undump end (#) ≡ (x > #) then goto bad fmt else undump end end define undump(#) ≡

begin undump int (x); if (x < #) ∨ undump end

define undump size end end (#) ≡ too small (#) else undump end end define undump size end (#) ≡

if x > # then undump size end end

define undump size(#) ≡ begin undump int (x); if x < # then goto bad fmt ; undump size end

1307. The next few sections of the program should make it clear how we use the dump/undump macros. ⟨ Dump constants for consistency check 1307 ⟩ ≡

dump int ( $); dump int (mem bot ); dump int (mem top); dump int (eqtb size); dump int (hash prime); dump int (hyph size)

This code is used in section 1302.

1308. Sections of a program that are “commented out” still contribute strings to the string pool; therefore and TEX will have the same strings. (And it is, of course, a good thing that they do.) ⟨ Undump constants for consistency check 1308 ⟩ ≡

x ← fmt file↑.int ; if x ≠ $ then goto bad fmt ; { check that strings are the same } undump int (x); if x ≠ mem bot then goto bad fmt ; undump int (x); if x ≠ mem top then goto bad fmt ; undump int (x); if x ≠ eqtb size then goto bad fmt ; undump int (x); if x ≠ hash prime then goto bad fmt ; undump int (x); if x ≠ hyph size then goto bad fmt

This code is used in section 1303.

1309. define dump four ASCII ≡ w.b0 ← qi (so(str pool [k])); w.b1 ← qi (so(str pool [k + 1]));

w.b2 ← qi (so(str pool [k + 2])); w.b3 ← qi (so(str pool [k + 3])); dump qqqq (w)

⟨ Dump the string pool 1309 ⟩ ≡ dump int (pool ptr ); dump int (str ptr ); for k ← 0 to str ptr do dump int (str start [k]); k ← 0; while k + 4 < pool ptr do

begin dump four ASCII ; k ← k + 4;

end; k ← pool ptr − 4; dump four ASCII ; print ln; print int (str ptr ); print ( ␣ ␣ ␣ ␣ ␣ ); print int (pool ptr )

This code is used in section 1302.

1310. define undump four ASCII ≡ undump qqqq (w); str pool [k] ← si (qo(w.b0 ));

str pool [k + 1] ← si (qo(w.b1 )); str pool [k + 2] ← si (qo(w.b2 )); str pool [k + 3] ← si (qo(w.b3 ))

⟨ Undump the string pool 1310 ⟩ ≡ undump size(0)(pool size)(´ ␣ ␣ ´ )(pool ptr ); undump size(0)(max strings )(´ ␣ ´ )(str ptr ); for k ← 0 to str ptr do undump(0)(pool ptr )(str start [k]); k ← 0; while k + 4 < pool ptr do

begin undump four ASCII ; k ← k + 4;

end; k ← pool ptr − 4; undump four ASCII ; init str ptr ← str ptr ; init pool ptr ← pool ptr

This code is used in section 1303.

1311. By sorting the list of available spaces in the variable-size portion of mem, we are usually able to get by without having to dump very much of the dynamic memory.

We recompute var used and dyn used , so that dumps valid information even when it has not been gathering statistics. ⟨ Dump the dynamic memory 1311 ⟩ ≡

sort avail ; var used ← 0; dump int (lo mem max ); dump int (rover ); p ← mem bot ; q ← rover ; x ← 0; repeat for k ← p to q + 1 do dump wd (mem[k]);

x ← x + q + 2 − p; var used ← var used + q − p; p ← q + node size(q); q ← rlink (q); until q = rover ; var used ← var used + lo mem max − p; dyn used ← mem end + 1 − hi mem min; for k ← p to lo mem max do dump wd (mem[k]); x ← x + lo mem max + 1 − p; dump int (hi mem min); dump int (avail ); for k ← hi mem min to mem end do dump wd (mem[k]); x ← x + mem end + 1 − hi mem min; p ← avail ; while p ≠ null do

begin decr (dyn used ); p ← link (p);

end; dump int (var used ); dump int (dyn used ); print ln; print int (x); print ( ␣ ␣ ␣ ␣ ␣ ␣ ␣ ); print int (var used ); print char ( ); print int (dyn used )

This code is used in section 1302.

1312. ⟨ Undump the dynamic memory 1312 ⟩ ≡ undump(lo mem stat max + 1000)(hi mem stat min − 1)(lo mem max ); undump(lo mem stat max + 1)(lo mem max )(rover ); p ← mem bot ; q ← rover ; repeat for k ← p to q + 1 do undump wd (mem[k]);

p ← q + node size(q);

if (p > lo mem max ) ∨ ((q ≥ rlink (q)) ∧ (rlink (q) ≠ rover )) then goto bad fmt ; q ← rlink (q);

until q = rover ; for k ← p to lo mem max do undump wd (mem[k]); if mem min < mem bot − 2 then { make more low memory available }

begin p ← llink (rover ); q ← mem min + 1; link (mem min) ← null ; info(mem min) ← null ;

{ we don’t use the bottom word } rlink (p) ← q; llink (rover ) ← q; rlink (q) ← rover ; llink (q) ← p; link (q) ← empty flag ; node size(q) ← mem bot − q; end;

undump(lo mem max + 1)(hi mem stat min)(hi mem min); undump(null )(mem top)(avail ); mem end ← mem top; for k ← hi mem min to mem end do undump wd (mem[k]); undump int (var used ); undump int (dyn used )

This code is used in section 1303.

1313. ⟨ Dump the table of equivalents 1313 ⟩ ≡ ⟨ Dump regions 1 to 4 of eqtb 1315 ⟩; ⟨ Dump regions 5 and 6 of eqtb 1316 ⟩; dump int (par loc); dump int (write loc); ⟨ Dump the hash table 1318 ⟩

This code is used in section 1302.

1314. ⟨ Undump the table of equivalents 1314 ⟩ ≡ ⟨ Undump regions 1 to 6 of eqtb 1317 ⟩; undump(hash base)(frozen control sequence)(par loc); par token ← cs token flag + par loc; undump(hash base)(frozen control sequence)(write loc); ⟨ Undump the hash table 1319 ⟩

This code is used in section 1303.

1315. The table of equivalents usually contains repeated information, so we dump it in compressed form: The sequence of n + 2 values (n, x_{1}, …, x_{n}, m) in the format file represents n + m consecutive entries of eqtb, with m extra copies of x_{n}, namely (x_{1}, …, x_{n}, x_{n}, …, x_{n}). ⟨ Dump regions 1 to 4 of eqtb 1315 ⟩ ≡

k ← active base; repeat j ← k;

while j < int base − 1 do

begin if (equiv (j) = equiv (j + 1)) ∧ (eq type(j) = eq type(j + 1)) ∧ (eq level (j) = eq level (j + 1))

then goto found1 ; incr (j); end;

l ← int base; goto done1 ; { j = int base − 1 } found1 : incr (j); l ← j;

while j < int base − 1 do

begin if (equiv (j) ≠ equiv (j + 1)) ∨ (eq type(j) ≠ eq type(j + 1)) ∨ (eq level (j) ≠ eq level (j + 1))

then goto done1 ; incr (j); end;

done1 : dump int (l − k);

while k < l do begin dump wd (eqtb[k]); incr (k); end;

k ← j + 1; dump int (k − l); until k = int base

This code is used in section 1313.

1316. ⟨ Dump regions 5 and 6 of eqtb 1316 ⟩ ≡

repeat j ← k;

while j < eqtb size do begin if eqtb[j].int = eqtb[j + 1].int then goto found2 ; incr (j); end;

l ← eqtb size + 1; goto done2 ; { j = eqtb size } found2 : incr (j); l ← j;

while j < eqtb size do begin if eqtb[j].int ≠ eqtb[j + 1].int then goto done2 ; incr (j); end;

done2 : dump int (l − k);

while k < l do begin dump wd (eqtb[k]); incr (k); end;

k ← j + 1; dump int (k − l); until k > eqtb size

This code is used in section 1313.

1317. ⟨ Undump regions 1 to 6 of eqtb 1317 ⟩ ≡ k ← active base; repeat undump int (x);

if (x < 1) ∨ (k + x > eqtb size + 1) then goto bad fmt ; for j ← k to k + x − 1 do undump wd (eqtb[j]); k ← k + x; undump int (x); if (x < 0) ∨ (k + x > eqtb size + 1) then goto bad fmt ; for j ← k to k + x − 1 do eqtb[j] ← eqtb[k − 1]; k ← k + x;

until k > eqtb size

This code is used in section 1314.

1318. A different scheme is used to compress the hash table, since its lower region is usually sparse. When text (p) ≠ 0 for p ≤ hash used , we output two words, p and hash[p]. The hash table is, of course, densely packed for p ≥ hash used , so the remaining entries are output in a block. ⟨ Dump the hash table 1318 ⟩ ≡

dump int (hash used ); cs count ← frozen control sequence − 1 − hash used ; for p ← hash base to hash used do

if text (p) ≠ 0 then begin dump int (p); dump hh(hash[p]); incr (cs count ); end;

for p ← hash used + 1 to undefined control sequence − 1 do dump hh(hash[p]); dump int (cs count ); print ln; print int (cs count ); print ( ␣ ␣ ␣ )

This code is used in section 1313.

1319. ⟨ Undump the hash table 1319 ⟩ ≡ undump(hash base)(frozen control sequence)(hash used ); p ← hash base − 1; repeat undump(p + 1)(hash used )(p); undump hh(hash[p]); until p = hash used ; for p ← hash used + 1 to undefined control sequence − 1 do undump hh(hash[p]); undump int (cs count )

This code is used in section 1314.

1320. ⟨ Dump the font information 1320 ⟩ ≡ dump int (fmem ptr ); for k ← 0 to fmem ptr − 1 do dump wd (font info[k]); dump int (font ptr ); for k ← null font to font ptr do ⟨ Dump the array info for internal font number k 1322 ⟩; print ln; print int (fmem ptr − 7); print ( ␣ ␣ ␣ ␣ ␣ ␣ ); print int (font ptr − font base); print ( ␣ ␣ ); if font ptr ≠ font base + 1 then print char ( )

This code is used in section 1302.

1321. ⟨ Undump the font information 1321 ⟩ ≡ undump size(7)(font mem size)(´ ␣ ␣ ´ )(fmem ptr ); for k ← 0 to fmem ptr − 1 do undump wd (font info[k]); undump size(font base)(font max )(´ ␣ ´ )(font ptr ); for k ← null font to font ptr do ⟨ Undump the array info for internal font number k 1323 ⟩

This code is used in section 1303.

1322. ⟨ Dump the array info for internal font number k 1322 ⟩ ≡ begin dump qqqq (font check [k]); dump int (font size[k]); dump int (font dsize[k]); dump int (font params [k]); dump int (hyphen char [k]); dump int (skew char [k]); dump int (font name[k]); dump int (font area[k]); dump int (font bc[k]); dump int (font ec[k]); dump int (char base[k]); dump int (width base[k]); dump int (height base[k]); dump int (depth base[k]); dump int (italic base[k]); dump int (lig kern base[k]); dump int (kern base[k]); dump int (exten base[k]); dump int (param base[k]); dump int (font glue[k]); dump int (bchar label [k]); dump int (font bchar [k]); dump int (font false bchar [k]); print nl ( ); print esc(font id text (k)); print char ( ); print file name(font name[k], font area[k], ); if font size[k] ≠ font dsize[k] then

begin print ( ␣ ␣ ); print scaled (font size[k]); print ( ); end;

end

This code is used in section 1320.

1323. ⟨ Undump the array info for internal font number k 1323 ⟩ ≡ begin undump qqqq (font check [k]); undump int (font size[k]); undump int (font dsize[k]); undump(min halfword )(max halfword )(font params [k]); undump int (hyphen char [k]); undump int (skew char [k]); undump(0)(str ptr )(font name[k]); undump(0)(str ptr )(font area[k]); undump(0)(255)(font bc[k]); undump(0)(255)(font ec[k]); undump int (char base[k]); undump int (width base[k]); undump int (height base[k]); undump int (depth base[k]); undump int (italic base[k]); undump int (lig kern base[k]); undump int (kern base[k]); undump int (exten base[k]); undump int (param base[k]); undump(min halfword )(lo mem max )(font glue[k]); undump(0)(fmem ptr − 1)(bchar label [k]); undump(min quarterword )(non char )(font bchar [k]); undump(min quarterword )(non char )(font false bchar [k]); end

This code is used in section 1321.

1324. ⟨ Dump the hyphenation tables 1324 ⟩ ≡ dump int (hyph count ); for k ← 0 to hyph size do

if hyph word [k] ≠ 0 then begin dump int (k); dump int (hyph word [k]); dump int (hyph list [k]); end;

print ln; print int (hyph count ); print ( ␣ ␣ ); if hyph count ≠ 1 then print char ( ); if trie not ready then init trie; dump int (trie max ); for k ← 0 to trie max do dump hh(trie[k]); dump int (trie op ptr ); for k ← 1 to trie op ptr do

begin dump int (hyf distance[k]); dump int (hyf num[k]); dump int (hyf next [k]); end;

print nl ( ␣ ␣ ␣ ␣ ); print int (trie max ); print ( ␣ ␣ ); print int (trie op ptr ); print ( ␣ ); if trie op ptr ≠ 1 then print char ( ); print ( ␣ ␣ ␣ ); print int (trie op size); for k ← 255 downto 0 do

if trie used [k] > min quarterword then begin print nl ( ␣␣ ); print int (qo(trie used [k])); print ( ␣ ␣ ␣ ); print int (k); dump int (k); dump int (qo(trie used [k])); end

This code is used in section 1302.

1325. Only “nonempty” parts of op start need to be restored. ⟨ Undump the hyphenation tables 1325 ⟩ ≡

undump(0)(hyph size)(hyph count ); for k ← 1 to hyph count do

begin undump(0)(hyph size)(j); undump(0)(str ptr )(hyph word [j]);

undump(min halfword )(max halfword )(hyph list [j]);

end; undump size(0)(trie size)(´ ␣ ´ )(j); init trie max ← j; tini for k ← 0 to j do undump hh(trie[k]); undump size(0)(trie op size)(´ ␣ ␣ ´ )(j); init trie op ptr ← j; tini for k ← 1 to j do

begin undump(0)(63)(hyf distance[k]); { a small number }

undump(0)(63)(hyf num[k]); undump(min quarterword )(max quarterword )(hyf next [k]); end;

init for k ← 0 to 255 do trie used [k] ← min quarterword ; tini k ← 256; while j > 0 do

begin undump(0)(k − 1)(k); undump(1)(j)(x); init trie used [k] ← qi (x); tini j ← j − x; op start [k] ← qo(j); end;

init trie not ready ← false tini

This code is used in section 1303.

1326. We have already printed a lot of statistics, so we set tracing stats ← 0 to prevent them appearing again. ⟨ Dump a couple more things and the closing check word 1326 ⟩ ≡

dump int (interaction); dump int (format ident ); dump int (69069); tracing stats ← 0

This code is used in section 1302.

1327. ⟨ Undump a couple more things and the closing check word 1327 ⟩ ≡ undump(batch mode)(error stop mode)(interaction); undump(0)(str ptr )(format ident ); undump int (x); if (x ≠ 69069) ∨ eof (fmt file) then goto bad fmt

This code is used in section 1303.

1328. ⟨ Create the format ident , open the format file, and inform the user that dumping has

begun 1328 ⟩ ≡ selector ← new string ; print ( ␣ ␣ ); print (job name); print char ( ␣ ); print int (year mod 100); print char ( ); print int (month); print char ( ); print int (day ); print char ( ); if interaction = batch mode then selector ← log only else selector ← term and log ; str room(1); format ident ← make string ; pack job name(format extension); while ¬w open out (fmt file) do prompt file name( ␣ ␣ , format extension); print nl ( ␣ ␣ ␣ ␣ ␣ ); slow print (w make name string (fmt file)); flush string ; print nl ( ); slow print (format ident )

This code is used in section 1302.

1329. ⟨ Close the format file 1329 ⟩ ≡

w close(fmt file)

This code is used in section 1302.