iname.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. /* Copyright (C) 1989, 1995, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved.
  2. This file is part of AFPL Ghostscript.
  3. AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author or
  4. distributor accepts any responsibility for the consequences of using it, or
  5. for whether it serves any particular purpose or works at all, unless he or
  6. she says so in writing. Refer to the Aladdin Free Public License (the
  7. "License") for full details.
  8. Every copy of AFPL Ghostscript must include a copy of the License, normally
  9. in a plain ASCII text file named PUBLIC. The License grants you the right
  10. to copy, modify and redistribute AFPL Ghostscript, but only under certain
  11. conditions described in the License. Among other things, the License
  12. requires that the copyright notice and this notice be preserved on all
  13. copies.
  14. */
  15. /*$Id: iname.c,v 1.3 2000/09/19 19:00:45 lpd Exp $ */
  16. /* Name lookup for Ghostscript interpreter */
  17. #include "memory_.h"
  18. #include "string_.h"
  19. #include "ghost.h"
  20. #include "gsstruct.h"
  21. #include "gxobj.h" /* for o_set_unmarked */
  22. #include "errors.h"
  23. #include "inamedef.h"
  24. #include "imemory.h" /* for isave.h */
  25. #include "isave.h"
  26. #include "store.h"
  27. /* Public values */
  28. const uint name_max_string = max_name_string;
  29. /* Define the permutation table for name hashing. */
  30. private const byte hash_permutation[256] = {
  31. NAME_HASH_PERMUTATION_DATA
  32. };
  33. /* Define the data for the 1-character names. */
  34. private const byte nt_1char_names[NT_1CHAR_SIZE] = {
  35. NT_1CHAR_NAMES_DATA
  36. };
  37. /* Structure descriptors */
  38. gs_private_st_simple(st_name_sub_table, name_sub_table, "name_sub_table");
  39. gs_private_st_composite(st_name_string_sub_table, name_string_sub_table_t,
  40. "name_string_sub_table_t",
  41. name_string_sub_enum_ptrs, name_string_sub_reloc_ptrs);
  42. gs_private_st_composite(st_name_table, name_table, "name_table",
  43. name_table_enum_ptrs, name_table_reloc_ptrs);
  44. /* Forward references */
  45. private int name_alloc_sub(P1(name_table *));
  46. private void name_free_sub(P2(name_table *, uint));
  47. private void name_scan_sub(P3(name_table *, uint, bool));
  48. /* Debugging printout */
  49. #ifdef DEBUG
  50. private void
  51. name_print(const char *msg, const name_table *nt, uint nidx, const int *pflag)
  52. {
  53. const name_string_t *pnstr = names_index_string_inline(nt, nidx);
  54. const name *pname = names_index_ptr_inline(nt, nidx);
  55. const byte *str = pnstr->string_bytes;
  56. dlprintf1("[n]%s", msg);
  57. if (pflag)
  58. dprintf1("(%d)", *pflag);
  59. dprintf2(" (0x%lx#%u)", (ulong)pname, nidx);
  60. debug_print_string(str, pnstr->string_size);
  61. dprintf2("(0x%lx,%u)\n", (ulong)str, pnstr->string_size);
  62. }
  63. # define if_debug_name(msg, nt, nidx, pflag)\
  64. if ( gs_debug_c('n') ) name_print(msg, nt, nidx, pflag)
  65. #else
  66. # define if_debug_name(msg, nt, nidx, pflag) DO_NOTHING
  67. #endif
  68. /* Initialize a name table */
  69. name_table *
  70. names_init(ulong count, gs_ref_memory_t *imem)
  71. {
  72. gs_memory_t *mem = (gs_memory_t *)imem;
  73. name_table *nt;
  74. int i;
  75. if (count == 0)
  76. count = max_name_count + 1L;
  77. else if (count - 1 > max_name_count)
  78. return 0;
  79. nt = gs_alloc_struct(mem, name_table, &st_name_table, "name_init(nt)");
  80. if (nt == 0)
  81. return 0;
  82. memset(nt, 0, sizeof(name_table));
  83. nt->max_sub_count =
  84. ((count - 1) | nt_sub_index_mask) >> nt_log2_sub_size;
  85. nt->name_string_attrs = imemory_space(imem) | a_readonly;
  86. nt->memory = mem;
  87. /* Initialize the one-character names. */
  88. /* Start by creating the necessary sub-tables. */
  89. for (i = 0; i < NT_1CHAR_FIRST + NT_1CHAR_SIZE; i += nt_sub_size) {
  90. int code = name_alloc_sub(nt);
  91. if (code < 0) {
  92. while (nt->sub_next > 0)
  93. name_free_sub(nt, --(nt->sub_next));
  94. gs_free_object(mem, nt, "name_init(nt)");
  95. return 0;
  96. }
  97. }
  98. for (i = -1; i < NT_1CHAR_SIZE; i++) {
  99. uint ncnt = NT_1CHAR_FIRST + i;
  100. uint nidx = name_count_to_index(ncnt);
  101. name *pname = names_index_ptr_inline(nt, nidx);
  102. name_string_t *pnstr = names_index_string_inline(nt, nidx);
  103. if (i < 0)
  104. pnstr->string_bytes = nt_1char_names,
  105. pnstr->string_size = 0;
  106. else
  107. pnstr->string_bytes = nt_1char_names + i,
  108. pnstr->string_size = 1;
  109. pnstr->foreign_string = 1;
  110. pnstr->mark = 1;
  111. pname->pvalue = pv_no_defn;
  112. }
  113. nt->perm_count = NT_1CHAR_FIRST + NT_1CHAR_SIZE;
  114. /* Reconstruct the free list. */
  115. nt->free = 0;
  116. names_trace_finish(nt, NULL);
  117. return nt;
  118. }
  119. /* Get the allocator for the name table. */
  120. gs_memory_t *
  121. names_memory(const name_table * nt)
  122. {
  123. return nt->memory;
  124. }
  125. /* Look up or enter a name in the table. */
  126. /* Return 0 or an error code. */
  127. /* The return may overlap the characters of the string! */
  128. /* See iname.h for the meaning of enterflag. */
  129. int
  130. names_ref(name_table *nt, const byte *ptr, uint size, ref *pref, int enterflag)
  131. {
  132. name *pname;
  133. name_string_t *pnstr;
  134. uint nidx;
  135. uint *phash;
  136. /* Compute a hash for the string. */
  137. /* Make a special check for 1-character names. */
  138. switch (size) {
  139. case 0:
  140. nidx = name_count_to_index(1);
  141. pname = names_index_ptr_inline(nt, nidx);
  142. goto mkn;
  143. case 1:
  144. if (*ptr < NT_1CHAR_SIZE) {
  145. uint hash = *ptr + NT_1CHAR_FIRST;
  146. nidx = name_count_to_index(hash);
  147. pname = names_index_ptr_inline(nt, nidx);
  148. goto mkn;
  149. }
  150. /* falls through */
  151. default: {
  152. uint hash;
  153. NAME_HASH(hash, hash_permutation, ptr, size);
  154. phash = nt->hash + (hash & (NT_HASH_SIZE - 1));
  155. }
  156. }
  157. for (nidx = *phash; nidx != 0;
  158. nidx = name_next_index(nidx, pnstr)
  159. ) {
  160. pnstr = names_index_string_inline(nt, nidx);
  161. if (pnstr->string_size == size &&
  162. !memcmp_inline(ptr, pnstr->string_bytes, size)
  163. ) {
  164. pname = name_index_ptr_inline(nt, nidx);
  165. goto mkn;
  166. }
  167. }
  168. /* Name was not in the table. Make a new entry. */
  169. if (enterflag < 0)
  170. return_error(e_undefined);
  171. if (size > max_name_string)
  172. return_error(e_limitcheck);
  173. nidx = nt->free;
  174. if (nidx == 0) {
  175. int code = name_alloc_sub(nt);
  176. if (code < 0)
  177. return code;
  178. nidx = nt->free;
  179. }
  180. pnstr = names_index_string_inline(nt, nidx);
  181. if (enterflag == 1) {
  182. byte *cptr = (byte *)gs_alloc_string(nt->memory, size,
  183. "names_ref(string)");
  184. if (cptr == 0)
  185. return_error(e_VMerror);
  186. memcpy(cptr, ptr, size);
  187. pnstr->string_bytes = cptr;
  188. pnstr->foreign_string = 0;
  189. } else {
  190. pnstr->string_bytes = ptr;
  191. pnstr->foreign_string = (enterflag == 0 ? 1 : 0);
  192. }
  193. pnstr->string_size = size;
  194. pname = name_index_ptr_inline(nt, nidx);
  195. pname->pvalue = pv_no_defn;
  196. nt->free = name_next_index(nidx, pnstr);
  197. set_name_next_index(nidx, pnstr, *phash);
  198. *phash = nidx;
  199. if_debug_name("new name", nt, nidx, &enterflag);
  200. mkn:
  201. make_name(pref, nidx, pname);
  202. return 0;
  203. }
  204. /* Get the string for a name. */
  205. void
  206. names_string_ref(const name_table * nt, const ref * pnref /* t_name */ ,
  207. ref * psref /* result, t_string */ )
  208. {
  209. const name_string_t *pnstr = names_string_inline(nt, pnref);
  210. make_const_string(psref,
  211. (pnstr->foreign_string ? avm_foreign | a_readonly :
  212. nt->name_string_attrs),
  213. pnstr->string_size,
  214. (const byte *)pnstr->string_bytes);
  215. }
  216. /* Convert a t_string object to a name. */
  217. /* Copy the executable attribute. */
  218. int
  219. names_from_string(name_table * nt, const ref * psref, ref * pnref)
  220. {
  221. int exec = r_has_attr(psref, a_executable);
  222. int code = names_ref(nt, psref->value.bytes, r_size(psref), pnref, 1);
  223. if (code < 0)
  224. return code;
  225. if (exec)
  226. r_set_attrs(pnref, a_executable);
  227. return code;
  228. }
  229. /* Enter a (permanently allocated) C string as a name. */
  230. int
  231. names_enter_string(name_table * nt, const char *str, ref * pref)
  232. {
  233. return names_ref(nt, (const byte *)str, strlen(str), pref, 0);
  234. }
  235. /* Invalidate the value cache for a name. */
  236. void
  237. names_invalidate_value_cache(name_table * nt, const ref * pnref)
  238. {
  239. pnref->value.pname->pvalue = pv_other;
  240. }
  241. /* Convert between names and indices. */
  242. #undef names_index
  243. name_index_t
  244. names_index(const name_table * nt, const ref * pnref)
  245. {
  246. return names_index_inline(nt, pnref);
  247. }
  248. void
  249. names_index_ref(const name_table * nt, name_index_t index, ref * pnref)
  250. {
  251. names_index_ref_inline(nt, index, pnref);
  252. }
  253. name *
  254. names_index_ptr(const name_table * nt, name_index_t index)
  255. {
  256. return names_index_ptr_inline(nt, index);
  257. }
  258. /* Get the index of the next valid name. */
  259. /* The argument is 0 or a valid index. */
  260. /* Return 0 if there are no more. */
  261. name_index_t
  262. names_next_valid_index(name_table * nt, name_index_t nidx)
  263. {
  264. const name_string_sub_table_t *ssub =
  265. nt->sub[nidx >> nt_log2_sub_size].strings;
  266. const name_string_t *pnstr;
  267. do {
  268. ++nidx;
  269. if ((nidx & nt_sub_index_mask) == 0)
  270. for (;; nidx += nt_sub_size) {
  271. if ((nidx >> nt_log2_sub_size) >= nt->sub_count)
  272. return 0;
  273. ssub = nt->sub[nidx >> nt_log2_sub_size].strings;
  274. if (ssub != 0)
  275. break;
  276. }
  277. pnstr = &ssub->strings[nidx & nt_sub_index_mask];
  278. }
  279. while (pnstr->string_bytes == 0);
  280. return nidx;
  281. }
  282. /* ------ Garbage collection ------ */
  283. /* Unmark all non-permanent names before a garbage collection. */
  284. void
  285. names_unmark_all(name_table * nt)
  286. {
  287. uint si;
  288. name_string_sub_table_t *ssub;
  289. for (si = 0; si < nt->sub_count; ++si)
  290. if ((ssub = nt->sub[si].strings) != 0) {
  291. uint i;
  292. /* We can make the test much more efficient if we want.... */
  293. for (i = 0; i < nt_sub_size; ++i)
  294. if (name_index_to_count((si << nt_log2_sub_size) + i) >=
  295. nt->perm_count)
  296. ssub->strings[i].mark = 0;
  297. }
  298. }
  299. /* Mark a name. Return true if new mark. We export this so we can mark */
  300. /* character names in the character cache. */
  301. bool
  302. names_mark_index(name_table * nt, name_index_t nidx)
  303. {
  304. name_string_t *pnstr = names_index_string_inline(nt, nidx);
  305. if (pnstr->mark)
  306. return false;
  307. pnstr->mark = 1;
  308. return true;
  309. }
  310. /* Get the object (sub-table) containing a name. */
  311. /* The garbage collector needs this so it can relocate pointers to names. */
  312. void /*obj_header_t */ *
  313. names_ref_sub_table(name_table * nt, const ref * pnref)
  314. {
  315. /* When this procedure is called, the pointers from the name table */
  316. /* to the sub-tables may or may not have been relocated already, */
  317. /* so we can't use them. Instead, we have to work backwards from */
  318. /* the name pointer itself. */
  319. return pnref->value.pname - (r_size(pnref) & nt_sub_index_mask);
  320. }
  321. void /*obj_header_t */ *
  322. names_index_sub_table(name_table * nt, name_index_t index)
  323. {
  324. return nt->sub[index >> nt_log2_sub_size].names;
  325. }
  326. void /*obj_header_t */ *
  327. names_index_string_sub_table(name_table * nt, name_index_t index)
  328. {
  329. return nt->sub[index >> nt_log2_sub_size].strings;
  330. }
  331. /*
  332. * Clean up the name table after the trace/mark phase of a garbage
  333. * collection, by removing names that aren't marked. gcst == NULL indicates
  334. * we're doing this for initialization or restore rather than for a GC.
  335. */
  336. void
  337. names_trace_finish(name_table * nt, gc_state_t * gcst)
  338. {
  339. uint *phash = &nt->hash[0];
  340. uint i;
  341. for (i = 0; i < NT_HASH_SIZE; phash++, i++) {
  342. name_index_t prev = 0;
  343. /*
  344. * The following initialization is only to pacify compilers:
  345. * pnprev is only referenced if prev has been set in the loop,
  346. * in which case pnprev is also set.
  347. */
  348. name_string_t *pnprev = 0;
  349. name_index_t nidx = *phash;
  350. while (nidx != 0) {
  351. name_string_t *pnstr = names_index_string_inline(nt, nidx);
  352. name_index_t next = name_next_index(nidx, pnstr);
  353. if (pnstr->mark) {
  354. prev = nidx;
  355. pnprev = pnstr;
  356. } else {
  357. if_debug_name("GC remove name", nt, nidx, NULL);
  358. /* Zero out the string data for the GC. */
  359. pnstr->string_bytes = 0;
  360. pnstr->string_size = 0;
  361. if (prev == 0)
  362. *phash = next;
  363. else
  364. set_name_next_index(prev, pnprev, next);
  365. }
  366. nidx = next;
  367. }
  368. }
  369. /* Reconstruct the free list. */
  370. nt->free = 0;
  371. for (i = nt->sub_count; i--;) {
  372. name_sub_table *sub = nt->sub[i].names;
  373. name_string_sub_table_t *ssub = nt->sub[i].strings;
  374. if (sub != 0) {
  375. name_scan_sub(nt, i, true);
  376. if (nt->sub[i].names == 0 && gcst != 0) {
  377. /* Mark the just-freed sub-table as unmarked. */
  378. o_set_unmarked((obj_header_t *)sub - 1);
  379. o_set_unmarked((obj_header_t *)ssub - 1);
  380. }
  381. }
  382. if (i == 0)
  383. break;
  384. }
  385. nt->sub_next = 0;
  386. }
  387. /* ------ Save/restore ------ */
  388. /* Clean up the name table before a restore. */
  389. /* Currently, this is never called, because the name table is allocated */
  390. /* in system VM. However, for a Level 1 system, we might choose to */
  391. /* allocate the name table in global VM; in this case, this routine */
  392. /* would be called before doing the global part of a top-level restore. */
  393. /* Currently we don't make any attempt to optimize this. */
  394. void
  395. names_restore(name_table * nt, alloc_save_t * save)
  396. {
  397. /* We simply mark all names older than the save, */
  398. /* and let names_trace_finish sort everything out. */
  399. uint si;
  400. for (si = 0; si < nt->sub_count; ++si)
  401. if (nt->sub[si].strings != 0) {
  402. uint i;
  403. for (i = 0; i < nt_sub_size; ++i) {
  404. name_string_t *pnstr =
  405. names_index_string_inline(nt, (si << nt_log2_sub_size) + i);
  406. if (pnstr->string_bytes == 0)
  407. pnstr->mark = 0;
  408. else if (pnstr->foreign_string) {
  409. /* Avoid storing into a read-only name string. */
  410. if (!pnstr->mark)
  411. pnstr->mark = 1;
  412. } else
  413. pnstr->mark =
  414. !alloc_is_since_save(pnstr->string_bytes, save);
  415. }
  416. }
  417. names_trace_finish(nt, NULL);
  418. }
  419. /* ------ Internal procedures ------ */
  420. /* Allocate the next sub-table. */
  421. private int
  422. name_alloc_sub(name_table * nt)
  423. {
  424. gs_memory_t *mem = nt->memory;
  425. uint sub_index = nt->sub_next;
  426. name_sub_table *sub;
  427. name_string_sub_table_t *ssub;
  428. for (;; ++sub_index) {
  429. if (sub_index > nt->max_sub_count)
  430. return_error(e_limitcheck);
  431. if (nt->sub[sub_index].names == 0)
  432. break;
  433. }
  434. nt->sub_next = sub_index + 1;
  435. if (nt->sub_next > nt->sub_count)
  436. nt->sub_count = nt->sub_next;
  437. sub = gs_alloc_struct(mem, name_sub_table, &st_name_sub_table,
  438. "name_alloc_sub(sub-table)");
  439. ssub = gs_alloc_struct(mem, name_string_sub_table_t,
  440. &st_name_string_sub_table,
  441. "name_alloc_sub(string sub-table)");
  442. if (sub == 0 || ssub == 0) {
  443. gs_free_object(mem, ssub, "name_alloc_sub(string sub-table)");
  444. gs_free_object(mem, sub, "name_alloc_sub(sub-table)");
  445. return_error(e_VMerror);
  446. }
  447. memset(sub, 0, sizeof(name_sub_table));
  448. memset(ssub, 0, sizeof(name_string_sub_table_t));
  449. /* The following code is only used if EXTEND_NAMES is non-zero. */
  450. #if name_extension_bits > 0
  451. sub->high_index = (sub_index >> (16 - nt_log2_sub_size)) << 16;
  452. #endif
  453. nt->sub[sub_index].names = sub;
  454. nt->sub[sub_index].strings = ssub;
  455. /* Add the newly allocated entries to the free list. */
  456. /* Note that the free list will only be properly sorted if */
  457. /* it was empty initially. */
  458. name_scan_sub(nt, sub_index, false);
  459. #ifdef DEBUG
  460. if (gs_debug_c('n')) { /* Print the lengths of the hash chains. */
  461. int i0;
  462. for (i0 = 0; i0 < NT_HASH_SIZE; i0 += 16) {
  463. int i;
  464. dlprintf1("[n]chain %d:", i0);
  465. for (i = i0; i < i0 + 16; i++) {
  466. int n = 0;
  467. uint nidx;
  468. for (nidx = nt->hash[i]; nidx != 0;
  469. nidx = name_next_index(nidx,
  470. names_index_string_inline(nt, nidx))
  471. )
  472. n++;
  473. dprintf1(" %d", n);
  474. }
  475. dputc('\n');
  476. }
  477. }
  478. #endif
  479. return 0;
  480. }
  481. /* Free a sub-table. */
  482. private void
  483. name_free_sub(name_table * nt, uint sub_index)
  484. {
  485. gs_free_object(nt->memory, nt->sub[sub_index].strings,
  486. "name_free_sub(string sub-table)");
  487. gs_free_object(nt->memory, nt->sub[sub_index].names,
  488. "name_free_sub(sub-table)");
  489. nt->sub[sub_index].names = 0;
  490. nt->sub[sub_index].strings = 0;
  491. }
  492. /* Scan a sub-table and add unmarked entries to the free list. */
  493. /* We add the entries in decreasing count order, so the free list */
  494. /* will stay sorted. If all entries are unmarked and free_empty is true, */
  495. /* free the sub-table. */
  496. private void
  497. name_scan_sub(name_table * nt, uint sub_index, bool free_empty)
  498. {
  499. name_string_sub_table_t *ssub = nt->sub[sub_index].strings;
  500. uint free = nt->free;
  501. uint nbase = sub_index << nt_log2_sub_size;
  502. uint ncnt = nbase + (nt_sub_size - 1);
  503. bool keep = !free_empty;
  504. if (ssub == 0)
  505. return;
  506. if (nbase == 0)
  507. nbase = 1, keep = true; /* don't free name 0 */
  508. for (;; --ncnt) {
  509. uint nidx = name_count_to_index(ncnt);
  510. name_string_t *pnstr = &ssub->strings[nidx & nt_sub_index_mask];
  511. if (pnstr->mark)
  512. keep = true;
  513. else {
  514. set_name_next_index(nidx, pnstr, free);
  515. free = nidx;
  516. }
  517. if (ncnt == nbase)
  518. break;
  519. }
  520. if (keep)
  521. nt->free = free;
  522. else {
  523. /* No marked entries, free the sub-table. */
  524. name_free_sub(nt, sub_index);
  525. if (sub_index == nt->sub_count - 1) {
  526. /* Back up over a final run of deleted sub-tables. */
  527. do {
  528. --sub_index;
  529. } while (nt->sub[sub_index].names == 0);
  530. nt->sub_count = sub_index + 1;
  531. if (nt->sub_next > sub_index)
  532. nt->sub_next = sub_index;
  533. } else if (nt->sub_next == sub_index)
  534. nt->sub_next--;
  535. }
  536. }
  537. /* Garbage collector enumeration and relocation procedures. */
  538. private
  539. ENUM_PTRS_BEGIN_PROC(name_table_enum_ptrs)
  540. {
  541. EV_CONST name_table *const nt = vptr;
  542. uint i = index >> 1;
  543. if (i >= nt->sub_count)
  544. return 0;
  545. if (index & 1)
  546. ENUM_RETURN(nt->sub[i].strings);
  547. else
  548. ENUM_RETURN(nt->sub[i].names);
  549. }
  550. ENUM_PTRS_END_PROC
  551. private RELOC_PTRS_WITH(name_table_reloc_ptrs, name_table *nt)
  552. {
  553. uint sub_count = nt->sub_count;
  554. uint i;
  555. /* Now we can relocate the sub-table pointers. */
  556. for (i = 0; i < sub_count; i++) {
  557. RELOC_VAR(nt->sub[i].names);
  558. RELOC_VAR(nt->sub[i].strings);
  559. }
  560. /*
  561. * We also need to relocate the cached value pointers.
  562. * We don't do this here, but in a separate scan over the
  563. * permanent dictionaries, at the very end of garbage collection.
  564. */
  565. }
  566. RELOC_PTRS_END
  567. private ENUM_PTRS_BEGIN_PROC(name_string_sub_enum_ptrs)
  568. {
  569. return 0;
  570. }
  571. ENUM_PTRS_END_PROC
  572. private RELOC_PTRS_BEGIN(name_string_sub_reloc_ptrs)
  573. {
  574. name_string_t *pnstr = ((name_string_sub_table_t *)vptr)->strings;
  575. uint i;
  576. for (i = 0; i < nt_sub_size; ++pnstr, ++i) {
  577. if (pnstr->string_bytes != 0 && !pnstr->foreign_string) {
  578. gs_const_string nstr;
  579. nstr.data = pnstr->string_bytes;
  580. nstr.size = pnstr->string_size;
  581. RELOC_CONST_STRING_VAR(nstr);
  582. pnstr->string_bytes = nstr.data;
  583. }
  584. }
  585. }
  586. RELOC_PTRS_END