/src/ghostpdl/psi/igcstr.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2024 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* String GC routines for Ghostscript */ |
18 | | #include "memory_.h" |
19 | | #include "ghost.h" |
20 | | #include "gsmdebug.h" |
21 | | #include "gsstruct.h" |
22 | | #include "iastate.h" |
23 | | #include "igcstr.h" |
24 | | #include "igc.h" |
25 | | |
26 | | /* Forward references */ |
27 | | static bool gc_mark_string(const byte *, uint, bool, const clump_t *); |
28 | | |
29 | | /* (Un)mark the strings in a clump. */ |
30 | | void |
31 | | gc_strings_set_marks(clump_t * cp, bool mark) |
32 | 3.36M | { |
33 | 3.36M | if (cp->smark != 0) { |
34 | 2.06M | if_debug3('6', "[6]clearing string marks "PRI_INTPTR"[%u] to %d\n", |
35 | 2.06M | (intptr_t)cp->smark, cp->smark_size, (int)mark); |
36 | 2.06M | memset(cp->smark, 0, cp->smark_size); |
37 | 2.06M | if (mark) |
38 | 180k | gc_mark_string(cp->sbase, (cp->climit - cp->sbase), true, cp); |
39 | 2.06M | } |
40 | 3.36M | } |
41 | | |
42 | | /* We mark strings a word at a time. */ |
43 | | typedef string_mark_unit bword; |
44 | | |
45 | 849M | #define bword_log2_bytes log2_sizeof_string_mark_unit |
46 | | #define bword_bytes (1 << bword_log2_bytes) |
47 | 849M | #define bword_log2_bits (bword_log2_bytes + 3) |
48 | 849M | #define bword_bits (1 << bword_log2_bits) |
49 | 658M | #define bword_1s (~(bword)0) |
50 | | /* Compensate for byte order reversal if necessary. */ |
51 | | #if ARCH_IS_BIG_ENDIAN |
52 | | # if bword_bytes == 2 |
53 | | # define bword_swap_bytes(m) m = (m << 8) | (m >> 8) |
54 | | # else /* bword_bytes == 4 */ |
55 | | # define bword_swap_bytes(m)\ |
56 | | m = (m << 24) | ((m & 0xff00) << 8) | ((m >> 8) & 0xff00) | (m >> 24) |
57 | | # endif |
58 | | #else |
59 | 383M | # define bword_swap_bytes(m) DO_NOTHING |
60 | | #endif |
61 | | |
62 | | /* (Un)mark a string in a known clump. Return true iff any new marks. */ |
63 | | static bool |
64 | | gc_mark_string(const byte * ptr, uint size, bool set, const clump_t * cp) |
65 | 131M | { |
66 | 131M | uint offset = ptr - cp->sbase; |
67 | 131M | bword *bp = (bword *) (cp->smark + ((offset & -bword_bits) >> 3)); |
68 | 131M | uint bn = offset & (bword_bits - 1); |
69 | 131M | bword m = (bword)(((uint64_t)bword_1s << bn) & bword_1s); |
70 | 131M | uint left = size; |
71 | 131M | bword marks = 0; |
72 | | |
73 | 131M | bword_swap_bytes(m); |
74 | 131M | if (set) { |
75 | 131M | if (left + bn >= bword_bits) { |
76 | 48.2M | marks |= ~*bp & m; |
77 | 48.2M | *bp |= m; |
78 | 48.2M | m = bword_1s, left -= bword_bits - bn, bp++; |
79 | 187M | while (left >= bword_bits) { |
80 | 139M | marks |= ~*bp; |
81 | 139M | *bp = bword_1s; |
82 | 139M | left -= bword_bits, bp++; |
83 | 139M | } |
84 | 48.2M | } |
85 | 131M | if (left) { |
86 | 126M | bword_swap_bytes(m); |
87 | 126M | m = (bword)(((uint64_t)m - ((uint64_t)m << left)) & bword_1s); |
88 | 126M | bword_swap_bytes(m); |
89 | 126M | marks |= ~*bp & m; |
90 | 126M | *bp |= m; |
91 | 126M | } |
92 | 131M | } else { |
93 | 0 | if (left + bn >= bword_bits) { |
94 | 0 | *bp &= ~m; |
95 | 0 | m = bword_1s, left -= bword_bits - bn, bp++; |
96 | 0 | if (left >= bword_bits * 5) { |
97 | 0 | memset(bp, 0, (left & -bword_bits) >> 3); |
98 | 0 | bp += left >> bword_log2_bits; |
99 | 0 | left &= bword_bits - 1; |
100 | 0 | } else |
101 | 0 | while (left >= bword_bits) { |
102 | 0 | *bp = 0; |
103 | 0 | left -= bword_bits, bp++; |
104 | 0 | } |
105 | 0 | } |
106 | 0 | if (left) { |
107 | 0 | bword_swap_bytes(m); |
108 | 0 | m = (bword)(((uint64_t)m - ((uint64_t)m << left)) & bword_1s); |
109 | 0 | bword_swap_bytes(m); |
110 | 0 | *bp &= ~m; |
111 | 0 | } |
112 | 0 | } |
113 | 131M | return marks != 0; |
114 | 131M | } |
115 | | |
116 | | #ifdef DEBUG |
117 | | /* Print a string for debugging. We need this because there is no d--- |
118 | | * equivalent of fwrite. |
119 | | */ |
120 | | static void |
121 | | dmfwrite(const gs_memory_t *mem, const byte *ptr, uint count) |
122 | | { |
123 | | uint i; |
124 | | for (i = 0; i < count; ++i) |
125 | | dmputc(mem, ptr[i]); |
126 | | } |
127 | | #endif |
128 | | |
129 | | /* Mark a string. Return true if any new marks. */ |
130 | | bool |
131 | | gc_string_mark(const byte * ptr, uint size, bool set, gc_state_t * gcst) |
132 | 132M | { |
133 | 132M | const clump_t *cp; |
134 | 132M | bool marks; |
135 | | |
136 | 132M | if (size == 0) |
137 | 1.08M | return false; |
138 | 131M | #define dmprintstr(mem)\ |
139 | 131M | dmputc(mem, '('); dmfwrite(mem, ptr, min(size, 20));\ |
140 | 131M | dmputs(mem, (size <= 20 ? ")" : "...)")) |
141 | 131M | if (!(cp = gc_locate(ptr, gcst))) { /* not in a clump */ |
142 | | #ifdef DEBUG |
143 | | if (gs_debug_c('5')) { |
144 | | dmlprintf2(gcst->heap, "[5]"PRI_INTPTR"[%u]", (intptr_t)ptr, size); |
145 | | dmprintstr(gcst->heap); |
146 | | dmputs(gcst->heap, " not in a clump\n"); |
147 | | } |
148 | | #endif |
149 | 0 | return false; |
150 | 0 | } |
151 | 131M | if (cp->smark == 0) /* not marking strings */ |
152 | 0 | return false; |
153 | | #ifdef DEBUG |
154 | | if (ptr < cp->ctop) { |
155 | | if_debug4('6', "String pointer "PRI_INTPTR"[%u] outside ["PRI_INTPTR".."PRI_INTPTR")\n", |
156 | | (intptr_t)ptr, size, (intptr_t)cp->ctop, (intptr_t)cp->climit); |
157 | | return false; |
158 | | } else if (ptr + size > cp->climit) { /* |
159 | | * If this is the bottommost string in a clump that has |
160 | | * an inner clump, the string's starting address is both |
161 | | * cp->ctop of the outer clump and cp->climit of the inner; |
162 | | * gc_locate may incorrectly attribute the string to the |
163 | | * inner clump because of this. This doesn't affect |
164 | | * marking or relocation, since the machinery for these |
165 | | * is all associated with the outermost clump, |
166 | | * but it can cause the validity check to fail. |
167 | | * Check for this case now. |
168 | | */ |
169 | | const clump_t *scp = cp; |
170 | | |
171 | | while (ptr == scp->climit && scp->outer != 0) |
172 | | scp = scp->outer; |
173 | | if (ptr + size > scp->climit) { |
174 | | if_debug4('6', "String pointer "PRI_INTPTR"[%u] outside ["PRI_INTPTR".."PRI_INTPTR")\n", |
175 | | (intptr_t)ptr, size, |
176 | | (intptr_t)scp->ctop, (intptr_t)scp->climit); |
177 | | return false; |
178 | | } |
179 | | } |
180 | | #endif |
181 | 131M | marks = gc_mark_string(ptr, size, set, cp); |
182 | | #ifdef DEBUG |
183 | | if (gs_debug_c('5')) { |
184 | | dmlprintf4(gcst->heap, "[5]%s%smarked "PRI_INTPTR"[%u]", |
185 | | (marks ? "" : "already "), (set ? "" : "un"), |
186 | | (intptr_t)ptr, size); |
187 | | dmprintstr(gcst->heap); |
188 | | dmputc(gcst->heap, '\n'); |
189 | | } |
190 | | #endif |
191 | 131M | return marks; |
192 | 131M | } |
193 | | |
194 | | /* Clear the relocation for strings. */ |
195 | | /* This requires setting the marks. */ |
196 | | void |
197 | | gc_strings_clear_reloc(clump_t * cp) |
198 | 131k | { |
199 | 131k | if (cp->sreloc != 0) { |
200 | 90.1k | gc_strings_set_marks(cp, true); |
201 | 90.1k | if_debug1('6', "[6]clearing string reloc "PRI_INTPTR"\n", |
202 | 90.1k | (intptr_t)cp->sreloc); |
203 | 90.1k | gc_strings_set_reloc(cp); |
204 | 90.1k | } |
205 | 131k | } |
206 | | |
207 | | /* Count the 0-bits in a byte. */ |
208 | | static const byte count_zero_bits_table[256] = |
209 | | { |
210 | | #define o4(n) n,n-1,n-1,n-2 |
211 | | #define o16(n) o4(n),o4(n-1),o4(n-1),o4(n-2) |
212 | | #define o64(n) o16(n),o16(n-1),o16(n-1),o16(n-2) |
213 | | o64(8), o64(7), o64(7), o64(6) |
214 | | }; |
215 | | |
216 | | #define byte_count_zero_bits(byt)\ |
217 | 303M | (uint)(count_zero_bits_table[byt]) |
218 | | #define byte_count_one_bits(byt)\ |
219 | 619M | (uint)(8 - count_zero_bits_table[byt]) |
220 | | |
221 | | /* Set the relocation for the strings in a clump. */ |
222 | | /* The sreloc table stores the relocated offset from climit for */ |
223 | | /* the beginning of each block of string_data_quantum characters. */ |
224 | | void |
225 | | gc_strings_set_reloc(clump_t * cp) |
226 | 3.23M | { |
227 | 3.23M | if (cp->sreloc != 0 && cp->smark != 0) { |
228 | 1.97M | byte *bot = cp->ctop; |
229 | 1.97M | byte *top = cp->climit; |
230 | 1.97M | uint count = |
231 | 1.97M | (top - bot + (string_data_quantum - 1)) >> |
232 | 1.97M | log2_string_data_quantum; |
233 | 1.97M | string_reloc_offset *relp = |
234 | 1.97M | cp->sreloc + |
235 | 1.97M | (cp->smark_size >> (log2_string_data_quantum - 3)); |
236 | 1.97M | register const byte *bitp = cp->smark + cp->smark_size; |
237 | 1.97M | register string_reloc_offset reloc = 0; |
238 | | |
239 | | /* Skip initial unrelocated strings quickly. */ |
240 | 1.97M | #if string_data_quantum == bword_bits || string_data_quantum == bword_bits * 2 |
241 | 1.97M | { |
242 | | /* Work around the alignment aliasing bug. */ |
243 | 1.97M | const bword *wp = (const bword *)bitp; |
244 | | |
245 | | #if string_data_quantum == bword_bits |
246 | | # define RELOC_TEST_1S(wp) (wp[-1]) |
247 | | #else /* string_data_quantum == bword_bits * 2 */ |
248 | 28.5M | # define RELOC_TEST_1S(wp) (wp[-1] & wp[-2]) |
249 | 1.97M | #endif |
250 | 29.3M | while (count && RELOC_TEST_1S(wp) == bword_1s) { |
251 | 27.3M | wp -= string_data_quantum / bword_bits; |
252 | 27.3M | *--relp = reloc += string_data_quantum; |
253 | 27.3M | --count; |
254 | 27.3M | } |
255 | 1.97M | #undef RELOC_TEST_1S |
256 | 1.97M | bitp = (const byte *)wp; |
257 | 1.97M | } |
258 | 1.97M | #endif |
259 | 39.9M | while (count--) { |
260 | 37.9M | bitp -= string_data_quantum / 8; |
261 | 37.9M | reloc += string_data_quantum - |
262 | 37.9M | byte_count_zero_bits(bitp[0]); |
263 | 37.9M | reloc -= byte_count_zero_bits(bitp[1]); |
264 | 37.9M | reloc -= byte_count_zero_bits(bitp[2]); |
265 | 37.9M | reloc -= byte_count_zero_bits(bitp[3]); |
266 | 37.9M | #if log2_string_data_quantum > 5 |
267 | 37.9M | reloc -= byte_count_zero_bits(bitp[4]); |
268 | 37.9M | reloc -= byte_count_zero_bits(bitp[5]); |
269 | 37.9M | reloc -= byte_count_zero_bits(bitp[6]); |
270 | 37.9M | reloc -= byte_count_zero_bits(bitp[7]); |
271 | 37.9M | #endif |
272 | 37.9M | *--relp = reloc; |
273 | 37.9M | } |
274 | 1.97M | } |
275 | 3.23M | cp->sdest = cp->climit; |
276 | 3.23M | } |
277 | | |
278 | | /* Relocate a string pointer. */ |
279 | | void |
280 | | igc_reloc_string(gs_string * sptr, gc_state_t * gcst) |
281 | 138M | { |
282 | 138M | byte *ptr; |
283 | 138M | const clump_t *cp; |
284 | 138M | uint offset; |
285 | 138M | uint reloc; |
286 | 138M | const byte *bitp; |
287 | 138M | byte byt; |
288 | | |
289 | 138M | if (sptr->size == 0) { |
290 | 1.08M | sptr->data = 0; |
291 | 1.08M | return; |
292 | 1.08M | } |
293 | 137M | ptr = sptr->data; |
294 | | |
295 | 137M | if (!(cp = gc_locate(ptr, gcst))) /* not in a clump */ |
296 | 0 | return; |
297 | 137M | if (cp->sreloc == 0 || cp->smark == 0) /* not marking strings */ |
298 | 0 | return; |
299 | 137M | offset = ptr - cp->sbase; |
300 | 137M | reloc = cp->sreloc[offset >> log2_string_data_quantum]; |
301 | 137M | bitp = &cp->smark[offset >> 3]; |
302 | 137M | switch (offset & (string_data_quantum - 8)) { |
303 | 0 | #if log2_string_data_quantum > 5 |
304 | 17.5M | case 56: |
305 | 17.5M | reloc -= byte_count_one_bits(bitp[-7]); |
306 | 34.4M | case 48: |
307 | 34.4M | reloc -= byte_count_one_bits(bitp[-6]); |
308 | 52.1M | case 40: |
309 | 52.1M | reloc -= byte_count_one_bits(bitp[-5]); |
310 | 69.6M | case 32: |
311 | 69.6M | reloc -= byte_count_one_bits(bitp[-4]); |
312 | 69.6M | #endif |
313 | 86.1M | case 24: |
314 | 86.1M | reloc -= byte_count_one_bits(bitp[-3]); |
315 | 103M | case 16: |
316 | 103M | reloc -= byte_count_one_bits(bitp[-2]); |
317 | 119M | case 8: |
318 | 119M | reloc -= byte_count_one_bits(bitp[-1]); |
319 | 137M | } |
320 | 137M | byt = *bitp & (0xff >> (8 - (offset & 7))); |
321 | 137M | reloc -= byte_count_one_bits(byt); |
322 | 137M | if_debug2('5', "[5]relocate string "PRI_INTPTR" to 0x%lx\n", |
323 | 137M | (intptr_t)ptr, (intptr_t)(cp->sdest - reloc)); |
324 | 137M | sptr->data = (cp->sdest - reloc); |
325 | 137M | } |
326 | | void |
327 | | igc_reloc_const_string(gs_const_string * sptr, gc_state_t * gcst) |
328 | 121M | { /* We assume the representation of byte * and const byte * is */ |
329 | | /* the same.... */ |
330 | 121M | igc_reloc_string((gs_string *) sptr, gcst); |
331 | 121M | } |
332 | | void |
333 | | igc_reloc_param_string(gs_param_string * sptr, gc_state_t * gcst) |
334 | 96.5k | { |
335 | 96.5k | if (!sptr->persistent) { |
336 | | /* We assume that gs_param_string is a subclass of gs_string. */ |
337 | 96.5k | igc_reloc_string((gs_string *)sptr, gcst); |
338 | 96.5k | } |
339 | 96.5k | } |
340 | | |
341 | | /* Compact the strings in a clump. */ |
342 | | void |
343 | | gc_strings_compact(clump_t * cp, const gs_memory_t *mem) |
344 | 3.14M | { |
345 | 3.14M | if (cp->smark != 0) { |
346 | 1.88M | byte *hi = cp->climit; |
347 | 1.88M | byte *lo = cp->ctop; |
348 | 1.88M | const byte *from = hi; |
349 | 1.88M | byte *to = hi; |
350 | 1.88M | const byte *bp = cp->smark + cp->smark_size; |
351 | | |
352 | | #ifdef DEBUG |
353 | | if (gs_debug_c('4') || gs_debug_c('5')) { |
354 | | byte *base = cp->sbase; |
355 | | uint i = (lo - base) & -string_data_quantum; |
356 | | uint n = ROUND_UP(hi - base, string_data_quantum); |
357 | | |
358 | | #define R 16 |
359 | | for (; i < n; i += R) { |
360 | | uint j; |
361 | | |
362 | | dmlprintf1(mem, "[4]"PRI_INTPTR": ", (intptr_t) (base + i)); |
363 | | for (j = i; j < i + R; j++) { |
364 | | byte ch = base[j]; |
365 | | |
366 | | if (ch <= 31) { |
367 | | dmputc(mem, '^'); |
368 | | dmputc(mem, ch + 0100); |
369 | | } else |
370 | | dmputc(mem, ch); |
371 | | } |
372 | | dmputc(mem, ' '); |
373 | | for (j = i; j < i + R; j++) |
374 | | dmputc(mem, (cp->smark[j >> 3] & (1 << (j & 7)) ? |
375 | | '+' : '.')); |
376 | | #undef R |
377 | | if (!(i & (string_data_quantum - 1))) |
378 | | dmprintf1(mem, " %u", cp->sreloc[i >> log2_string_data_quantum]); |
379 | | dmputc(mem, '\n'); |
380 | | } |
381 | | } |
382 | | #endif |
383 | | /* |
384 | | * Skip unmodified strings quickly. We know that cp->smark is |
385 | | * aligned to a string_mark_unit. |
386 | | */ |
387 | 1.88M | { |
388 | | /* Work around the alignment aliasing bug. */ |
389 | 1.88M | const bword *wp = (const bword *)bp; |
390 | | |
391 | 53.6M | while (to > lo && wp[-1] == bword_1s) |
392 | 51.7M | to -= bword_bits, --wp; |
393 | 1.88M | bp = (const byte *)wp; |
394 | 2.84M | while (to > lo && bp[-1] == 0xff) |
395 | 960k | to -= 8, --bp; |
396 | 1.88M | } |
397 | 1.88M | from = to; |
398 | | |
399 | 298M | while (from > lo) { |
400 | 296M | byte b = *--bp; |
401 | | |
402 | 296M | from -= 8; |
403 | 296M | switch (b) { |
404 | 48.1M | case 0xff: |
405 | 48.1M | to -= 8; |
406 | | /* |
407 | | * Since we've seen a byte other than 0xff, we know |
408 | | * to != from at this point. |
409 | | */ |
410 | 48.1M | to[7] = from[7]; |
411 | 48.1M | to[6] = from[6]; |
412 | 48.1M | to[5] = from[5]; |
413 | 48.1M | to[4] = from[4]; |
414 | 48.1M | to[3] = from[3]; |
415 | 48.1M | to[2] = from[2]; |
416 | 48.1M | to[1] = from[1]; |
417 | 48.1M | to[0] = from[0]; |
418 | 48.1M | break; |
419 | 3.90M | default: |
420 | 3.90M | if (b & 0x80) |
421 | 2.30M | *--to = from[7]; |
422 | 3.90M | if (b & 0x40) |
423 | 2.25M | *--to = from[6]; |
424 | 3.90M | if (b & 0x20) |
425 | 2.17M | *--to = from[5]; |
426 | 3.90M | if (b & 0x10) |
427 | 2.13M | *--to = from[4]; |
428 | 3.90M | if (b & 8) |
429 | 1.99M | *--to = from[3]; |
430 | 3.90M | if (b & 4) |
431 | 1.77M | *--to = from[2]; |
432 | 3.90M | if (b & 2) |
433 | 1.51M | *--to = from[1]; |
434 | 3.90M | if (b & 1) |
435 | 1.57M | *--to = from[0]; |
436 | | /* falls through */ |
437 | 248M | case 0: |
438 | 248M | ; |
439 | 296M | } |
440 | 296M | } |
441 | 1.88M | gs_alloc_fill(cp->ctop, gs_alloc_fill_collected, |
442 | 1.88M | to - cp->ctop); |
443 | 1.88M | cp->ctop = to; |
444 | 1.88M | } |
445 | 3.14M | } |