/src/ghostpdl/base/gxccman.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2025 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 | | /* Character cache management routines for Ghostscript library */ |
18 | | #include "gx.h" |
19 | | #include "memory_.h" |
20 | | #include "gpcheck.h" |
21 | | #include "gserrors.h" |
22 | | #include "gsstruct.h" |
23 | | #include "gsbitops.h" |
24 | | #include "gsutil.h" /* for gs_next_ids */ |
25 | | #include "gxfixed.h" |
26 | | #include "gxmatrix.h" |
27 | | #include "gzstate.h" |
28 | | #include "gxpath.h" |
29 | | #include "gxdevice.h" |
30 | | #include "gxdevmem.h" |
31 | | #include "gxchar.h" |
32 | | #include "gxfont.h" |
33 | | #include "gxfcache.h" |
34 | | #include "gxxfont.h" |
35 | | #include "gxttfb.h" |
36 | | #include "gxfont42.h" |
37 | | #include "gxobj.h" |
38 | | |
39 | | /* Define the descriptors for the cache structures. */ |
40 | | private_st_cached_fm_pair(); |
41 | | private_st_cached_fm_pair_elt(); |
42 | | /*private_st_cached_char(); *//* unused */ |
43 | | private_st_cached_char_ptr(); /* unused */ |
44 | | private_st_cached_char_ptr_elt(); |
45 | | /* |
46 | | * Define a descriptor for the cache data. This is equivalent to st_bytes, |
47 | | * but it identifies the cache data as such in a memory dump. |
48 | | */ |
49 | | gs_private_st_simple(st_font_cache_bytes, byte, "font cache bytes"); |
50 | | /* GC procedures */ |
51 | | /* We do all the work in font_dir_enum/reloc_ptrs in gsfont.c. */ |
52 | | /* See gxfcache.h for details. */ |
53 | | static |
54 | 411k | ENUM_PTRS_BEGIN(cc_ptr_enum_ptrs) return 0; |
55 | | |
56 | 411k | ENUM_PTRS_END |
57 | 6.73G | static RELOC_PTRS_BEGIN(cc_ptr_reloc_ptrs) |
58 | 6.73G | { |
59 | 6.73G | } |
60 | 6.73G | RELOC_PTRS_END |
61 | | |
62 | | /* Forward references */ |
63 | | static int alloc_char(gs_font_dir *, ulong, cached_char **); |
64 | | static int alloc_char_in_chunk(gs_font_dir *, ulong, cached_char **); |
65 | | static void hash_remove_cached_char(gs_font_dir *, uint); |
66 | | static void shorten_cached_char(gs_font_dir *, cached_char *, uint); |
67 | | |
68 | | /* ====== Initialization ====== */ |
69 | | |
70 | | /* Allocate and initialize the character cache elements of a font directory. */ |
71 | | int |
72 | | gx_char_cache_alloc(gs_memory_t * struct_mem, gs_memory_t * bits_mem, |
73 | | gs_font_dir * pdir, uint bmax, uint mmax, uint cmax, uint upper) |
74 | 291k | { /* Since we use open hashing, we must increase cmax somewhat. */ |
75 | 291k | uint chsize = (cmax + (cmax >> 1)) | 31; |
76 | 291k | cached_fm_pair *mdata; |
77 | 291k | cached_char **chars; |
78 | | |
79 | | /* the table size must be adjusted upward such that we overflow |
80 | | cache character memory before filling the table. The searching |
81 | | code uses an empty table entry as a sentinel. */ |
82 | 291k | chsize = max(chsize, ROUND_UP(bmax, sizeof_cached_char) / sizeof_cached_char + 1); |
83 | | |
84 | | /* Round up chsize to a power of 2. */ |
85 | 1.16M | while (chsize & (chsize + 1)) |
86 | 873k | chsize |= chsize >> 1; |
87 | 291k | chsize++; |
88 | 291k | mdata = gs_alloc_struct_array(struct_mem, mmax, cached_fm_pair, |
89 | 291k | &st_cached_fm_pair_element, |
90 | 291k | "font_dir_alloc(mdata)"); |
91 | 291k | chars = gs_alloc_struct_array(struct_mem, chsize, cached_char *, |
92 | 291k | &st_cached_char_ptr_element, |
93 | 291k | "font_dir_alloc(chars)"); |
94 | 291k | if (mdata == 0 || chars == 0) { |
95 | 0 | gs_free_object(struct_mem, chars, "font_dir_alloc(chars)"); |
96 | 0 | gs_free_object(struct_mem, mdata, "font_dir_alloc(mdata)"); |
97 | 0 | return_error(gs_error_VMerror); |
98 | 0 | } |
99 | 291k | pdir->fmcache.mmax = mmax; |
100 | 291k | pdir->fmcache.mdata = mdata; |
101 | 291k | memset(mdata, 0, mmax * sizeof(*mdata)); |
102 | 291k | memset(chars, 0, chsize * sizeof(*chars)); |
103 | 291k | pdir->ccache.struct_memory = struct_mem; |
104 | 291k | pdir->ccache.bits_memory = bits_mem; |
105 | 291k | pdir->ccache.bmax = bmax; |
106 | 291k | pdir->ccache.cmax = cmax; |
107 | 291k | pdir->ccache.lower = upper / 10; |
108 | 291k | pdir->ccache.upper = upper; |
109 | 291k | pdir->ccache.table = chars; |
110 | 291k | pdir->ccache.table_mask = chsize - 1; |
111 | 291k | return gx_char_cache_init(pdir); |
112 | 291k | } |
113 | | |
114 | | /* Initialize the character cache. */ |
115 | | int |
116 | | gx_char_cache_init(register gs_font_dir * dir) |
117 | 291k | { |
118 | 291k | int i; |
119 | 291k | cached_fm_pair *pair; |
120 | 291k | char_cache_chunk *cck = (char_cache_chunk *) |
121 | 291k | gs_alloc_bytes_immovable(dir->ccache.bits_memory, |
122 | 291k | sizeof(char_cache_chunk), |
123 | 291k | "initial_chunk"); |
124 | 291k | if (cck == NULL) |
125 | 0 | return_error(gs_error_VMerror); |
126 | | |
127 | 291k | dir->fmcache.msize = 0; |
128 | 291k | dir->fmcache.used = dir->fmcache.mmax; |
129 | 291k | dir->fmcache.free = dir->fmcache.mmax; |
130 | 291k | dir->fmcache.unused = 0; |
131 | 291k | gx_bits_cache_chunk_init(cck, NULL, 0); |
132 | 291k | gx_bits_cache_init((gx_bits_cache *) & dir->ccache, cck); |
133 | 291k | dir->ccache.bspace = 0; |
134 | 291k | memset((char *)dir->ccache.table, 0, |
135 | 291k | (dir->ccache.table_mask + 1) * sizeof(cached_char *)); |
136 | 291k | for (i = 0, pair = dir->fmcache.mdata; |
137 | 58.5M | i < dir->fmcache.mmax; i++, pair++) { |
138 | 58.2M | pair->index = i; |
139 | 58.2M | fm_pair_init(pair); |
140 | 58.2M | pair->ttf = 0; |
141 | 58.2M | pair->ttr = 0; |
142 | 58.2M | } |
143 | 291k | return 0; |
144 | 291k | } |
145 | | |
146 | | /* ====== Purging ====== */ |
147 | | |
148 | | /* Purge from the character cache all entries selected by */ |
149 | | /* a client-supplied procedure. */ |
150 | | void |
151 | | gx_purge_selected_cached_chars(gs_font_dir * dir, |
152 | | bool(*proc) (const gs_memory_t *mem, |
153 | | cached_char *, void *), |
154 | | void *proc_data) |
155 | 1.79M | { |
156 | 1.79M | int chi; |
157 | 1.79M | int cmax = dir->ccache.table_mask; |
158 | | |
159 | 29.3G | for (chi = 0; chi <= cmax;) { |
160 | 29.3G | cached_char *cc = dir->ccache.table[chi]; |
161 | | |
162 | 29.3G | if (cc != 0 && |
163 | 29.3G | (*proc) (dir->memory, cc, proc_data)) { |
164 | 6.04M | hash_remove_cached_char(dir, chi); |
165 | 6.04M | gx_free_cached_char(dir, cc); |
166 | 6.04M | } else |
167 | 29.3G | chi++; |
168 | 29.3G | } |
169 | 1.79M | } |
170 | | |
171 | | /* ====== font-matrix pair lists ====== */ |
172 | | |
173 | | static int |
174 | | fm_pair_remove_from_list(gs_font_dir * dir, cached_fm_pair *pair, uint *head) |
175 | 3.96M | { |
176 | 3.96M | if (dir->fmcache.mdata + pair->index != pair) |
177 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
178 | 3.96M | if (pair->next == pair->index) { |
179 | | /* The list consists of single element. */ |
180 | 1.76M | if (pair->prev != pair->index) |
181 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
182 | 1.76M | *head = dir->fmcache.mmax; |
183 | 2.19M | } else { |
184 | 2.19M | cached_fm_pair *next = dir->fmcache.mdata + pair->next; |
185 | 2.19M | cached_fm_pair *prev = dir->fmcache.mdata + pair->prev; |
186 | | |
187 | 2.19M | if (next->prev != pair->index) |
188 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
189 | 2.19M | if (prev->next != pair->index) |
190 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
191 | 2.19M | if (*head == pair->index) |
192 | 345k | *head = next->index; |
193 | 2.19M | next->prev = prev->index; |
194 | 2.19M | prev->next = next->index; |
195 | 2.19M | } |
196 | 3.96M | return 0; |
197 | 3.96M | } |
198 | | |
199 | | static int |
200 | | fm_pair_insert_into_list(gs_font_dir * dir, cached_fm_pair *pair, uint *head) |
201 | 4.27M | { |
202 | 4.27M | if (dir->fmcache.mdata + pair->index != pair) |
203 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
204 | 4.27M | if (*head >= dir->fmcache.mmax) { |
205 | 1.82M | *head = pair->next = pair->prev = pair->index; |
206 | 2.45M | } else { |
207 | 2.45M | cached_fm_pair *first = dir->fmcache.mdata + *head; |
208 | 2.45M | cached_fm_pair *last = dir->fmcache.mdata + first->prev; |
209 | | |
210 | 2.45M | if (first->prev != last->index) |
211 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
212 | 2.45M | if (last->next != first->index) |
213 | 0 | return_error(gs_error_unregistered); /* Must not happen. */ |
214 | 2.45M | pair->next = first->index; |
215 | 2.45M | pair->prev = last->index; |
216 | 2.45M | first->prev = last->next = pair->index; |
217 | 2.45M | *head = pair->index; |
218 | 2.45M | } |
219 | 4.27M | return 0; |
220 | 4.27M | } |
221 | | |
222 | | /* ====== Font-level routines ====== */ |
223 | | |
224 | | static int |
225 | | gx_attach_tt_interpreter(gs_font_dir * dir, |
226 | | gs_font_type42 *font, cached_fm_pair *pair, |
227 | | const gs_matrix * char_tm, const gs_log2_scale_point *log2_scale, |
228 | | bool design_grid) |
229 | 2.40k | { |
230 | 2.40k | float cxx, cxy, cyx, cyy; |
231 | 2.40k | gs_matrix m; |
232 | 2.40k | int code; |
233 | | |
234 | 2.40k | gx_compute_char_matrix(char_tm, log2_scale, &cxx, &cxy, &cyx, &cyy); |
235 | 2.40k | pair->design_grid = design_grid; |
236 | 2.40k | m.xx = cxx; |
237 | 2.40k | m.xy = cxy; |
238 | 2.40k | m.yx = cyx; |
239 | 2.40k | m.yy = cyy; |
240 | 2.40k | m.tx = m.ty = 0; |
241 | 2.40k | pair->ttr = gx_ttfReader__create(dir->memory->stable_memory); |
242 | 2.40k | if (!pair->ttr) |
243 | 0 | return_error(gs_error_VMerror); |
244 | | /* We could use a single the reader instance for all fonts ... */ |
245 | 2.40k | pair->ttf = ttfFont__create(dir); |
246 | 2.40k | if (!pair->ttf) |
247 | 0 | return_error(gs_error_VMerror); |
248 | 2.40k | gx_ttfReader__set_font(pair->ttr, (gs_font_type42 *)font); |
249 | 2.40k | code = ttfFont__Open_aux(pair->ttf, dir->tti, pair->ttr, |
250 | 2.40k | (gs_font_type42 *)font, &m, log2_scale, design_grid); |
251 | 2.40k | gx_ttfReader__set_font(pair->ttr, NULL); |
252 | 2.40k | return code; |
253 | 2.40k | } |
254 | | |
255 | | static inline bool |
256 | | does_font_need_tt_interpreter(gs_font *font) |
257 | 24.8M | { |
258 | 24.8M | if (font->FontType == ft_TrueType || font->FontType == ft_CID_TrueType) { |
259 | 3.77M | gs_font_type42 *pfont = (gs_font_type42 *)font; |
260 | | |
261 | 3.77M | if (pfont->FAPI==NULL) |
262 | 55.6k | return true; |
263 | 3.77M | } |
264 | 24.7M | return false; |
265 | 24.8M | } |
266 | | |
267 | | int |
268 | | gx_provide_fm_pair_attributes(gs_font_dir * dir, |
269 | | gs_font *font, cached_fm_pair *pair, |
270 | | const gs_matrix * char_tm, const gs_log2_scale_point *log2_scale, |
271 | | bool design_grid) |
272 | 23.0M | { |
273 | 23.0M | if (does_font_need_tt_interpreter(font)) { |
274 | 53.2k | if (pair->ttf != NULL) |
275 | 53.2k | return 0; /* Already attached. */ |
276 | 0 | return gx_attach_tt_interpreter(dir, (gs_font_type42 *)font, pair, |
277 | 0 | char_tm, log2_scale, design_grid); |
278 | 53.2k | } |
279 | 22.9M | return 0; |
280 | 23.0M | } |
281 | | |
282 | | /* Add a font/matrix pair to the cache. */ |
283 | | /* (This is only exported for gxccache.c.) */ |
284 | | int |
285 | | gx_add_fm_pair(register gs_font_dir * dir, gs_font * font, const gs_uid * puid, |
286 | | const gs_matrix * char_tm, const gs_log2_scale_point *log2_scale, |
287 | | bool design_grid, cached_fm_pair **ppair) |
288 | 1.80M | { |
289 | 1.80M | float mxx, mxy, myx, myy; |
290 | 1.80M | register cached_fm_pair *pair; |
291 | 1.80M | int code; |
292 | | |
293 | 1.80M | gx_compute_ccache_key(font, char_tm, log2_scale, design_grid, |
294 | 1.80M | &mxx, &mxy, &myx, &myy); |
295 | 1.80M | if (dir->fmcache.msize == dir->fmcache.mmax) { |
296 | | /* cache is full, drop the older entry. */ |
297 | | /* gx_touch_fm_pair must be called whenever |
298 | | a pair is used to move it to the top of the list. |
299 | | Since we drop a pair from the list bottom, |
300 | | and since the list is long enough, |
301 | | with a high probability it won't drop a pair, |
302 | | which currently is pointed by an active text enumerator. |
303 | | |
304 | | Note that with Type 3 fonts multiple text enumerators |
305 | | may be active (exist on estack) in same time, |
306 | | therefore the list length sets a constraint for |
307 | | the number of font-matrix pairs used within a charproc. |
308 | | If it uses too many ones, the outer text enumerator |
309 | | will fail with 'invalidfont' in gx_add_cached_char. |
310 | | */ |
311 | 919k | pair = dir->fmcache.mdata + dir->fmcache.used; |
312 | 919k | pair = dir->fmcache.mdata + pair->prev; /* last touched. */ |
313 | 919k | code = gs_purge_fm_pair(dir, pair, 0); |
314 | 919k | if (code < 0) |
315 | 0 | return code; |
316 | 919k | } |
317 | 1.80M | if (dir->fmcache.free < dir->fmcache.mmax) { |
318 | | /* use a free entry. */ |
319 | 1.49M | pair = dir->fmcache.mdata + dir->fmcache.free; |
320 | 1.49M | code = fm_pair_remove_from_list(dir, pair, &dir->fmcache.free); |
321 | 1.49M | if (code < 0) |
322 | 0 | return code; |
323 | 1.49M | } else { |
324 | | /* reserve a new entry. */ |
325 | 311k | pair = dir->fmcache.mdata + dir->fmcache.unused; |
326 | 311k | dir->fmcache.unused++; |
327 | 311k | } |
328 | 1.80M | font->is_cached = true; /* Set this early to ensure |
329 | | gs_purge_font_from_char_caches works for it in case of errors. */ |
330 | 1.80M | dir->fmcache.msize++; |
331 | 1.80M | code = fm_pair_insert_into_list(dir, pair, &dir->fmcache.used); |
332 | 1.80M | if (code < 0) |
333 | 0 | return code; |
334 | 1.80M | pair->font = font; |
335 | 1.80M | pair->UID = *puid; |
336 | | /* Copy UID into a stable memory, |
337 | | so that 'restore' may keep this pair. */ |
338 | 1.80M | code = uid_copy(&pair->UID, dir->memory->stable_memory, "gx_add_fm_pair"); |
339 | 1.80M | if (code < 0) { |
340 | 0 | uid_set_invalid(&pair->UID); |
341 | 0 | return code; |
342 | 0 | } |
343 | 1.80M | pair->FontType = font->FontType; |
344 | 1.80M | pair->hash = (uint) (dir->hash % 549); /* scramble bits */ |
345 | 1.80M | dir->hash += 371; |
346 | 1.80M | pair->mxx = mxx, pair->mxy = mxy; |
347 | 1.80M | pair->myx = myx, pair->myy = myy; |
348 | 1.80M | pair->num_chars = 0; |
349 | 1.80M | pair->xfont_tried = false; |
350 | 1.80M | pair->xfont = 0; |
351 | 1.80M | pair->ttf = 0; |
352 | 1.80M | pair->ttr = 0; |
353 | 1.80M | pair->design_grid = false; |
354 | 1.80M | if (does_font_need_tt_interpreter(font)) { |
355 | 2.40k | code = gx_attach_tt_interpreter(dir, (gs_font_type42 *)font, pair, |
356 | 2.40k | char_tm, log2_scale, design_grid); |
357 | 2.40k | if (code < 0) |
358 | 0 | return code; |
359 | 2.40k | } |
360 | 1.80M | else { |
361 | 1.80M | if (font->FontType == ft_TrueType) { |
362 | 20.9k | pair->design_grid = design_grid; |
363 | 20.9k | } |
364 | 1.80M | } |
365 | 1.80M | pair->memory = 0; |
366 | 1.80M | if_debug8m('k', dir->memory, |
367 | 1.80M | "[k]adding pair "PRI_INTPTR": font="PRI_INTPTR" [%g %g %g %g] UID %ld, "PRI_INTPTR"\n", |
368 | 1.80M | (intptr_t)pair, (intptr_t)font, |
369 | 1.80M | pair->mxx, pair->mxy, pair->myx, pair->myy, |
370 | 1.80M | (long)pair->UID.id, (intptr_t) pair->UID.xvalues); |
371 | 1.80M | *ppair = pair; |
372 | 1.80M | return 0; |
373 | 1.80M | } |
374 | | |
375 | | /* Update the pointer to the last used font/matrix pair. */ |
376 | | int |
377 | | gx_touch_fm_pair(gs_font_dir *dir, cached_fm_pair *pair) |
378 | 23.0M | { |
379 | 23.0M | if (pair->index != dir->fmcache.used) { |
380 | 772k | int code; |
381 | | |
382 | 772k | code = fm_pair_remove_from_list(dir, pair, &dir->fmcache.used); |
383 | 772k | if (code < 0) |
384 | 0 | return code; |
385 | 772k | return fm_pair_insert_into_list(dir, pair, &dir->fmcache.used); |
386 | 772k | } |
387 | 22.2M | return 0; |
388 | 23.0M | } |
389 | | |
390 | | /* ------ Internal routines ------ */ |
391 | | |
392 | | /* Purge from the caches all references to a given font/matrix pair, */ |
393 | | /* or just characters that depend on its xfont. */ |
394 | 47.5M | #define cpair ((cached_fm_pair *)vpair) |
395 | | static bool |
396 | | purge_fm_pair_char(const gs_memory_t *mem, cached_char * cc, void *vpair) |
397 | 47.5M | { |
398 | 47.5M | return cc_pair(cc) == cpair; |
399 | 47.5M | } |
400 | | #undef cpair |
401 | | |
402 | | static inline void |
403 | | gs_clean_fm_pair_attributes(gs_font_dir * dir, cached_fm_pair * pair) |
404 | 1.80M | { |
405 | 1.80M | if (pair->ttr) |
406 | 2.40k | gx_ttfReader__destroy(pair->ttr); |
407 | 1.80M | pair->ttr = 0; |
408 | 1.80M | if (pair->ttf) |
409 | 2.40k | ttfFont__destroy(pair->ttf, dir); |
410 | 1.80M | pair->ttf = 0; |
411 | 1.80M | } |
412 | | |
413 | | void |
414 | | gs_clean_fm_pair(gs_font_dir * dir, cached_fm_pair * pair) |
415 | 109k | { |
416 | 109k | if_debug1m('k', dir->memory, "[k]cleaning pair "PRI_INTPTR"\n", (intptr_t) pair); |
417 | 109k | pair->font = NULL; |
418 | 109k | gs_clean_fm_pair_attributes(dir, pair); |
419 | 109k | } |
420 | | |
421 | | int |
422 | | gs_purge_fm_pair(gs_font_dir * dir, cached_fm_pair * pair, int xfont_only) |
423 | 1.69M | { |
424 | 1.69M | if_debug2m('k', dir->memory, "[k]purging pair "PRI_INTPTR"%s\n", |
425 | 1.69M | (intptr_t)pair, (xfont_only ? " (xfont only)" : "")); |
426 | 1.69M | if (pair->xfont != 0) { |
427 | 0 | (*pair->xfont->common.procs->release) (pair->xfont, |
428 | 0 | pair->memory); |
429 | 0 | pair->xfont_tried = false; |
430 | 0 | pair->xfont = 0; |
431 | 0 | } |
432 | 1.69M | gx_purge_selected_cached_chars(dir, |
433 | 1.69M | purge_fm_pair_char, |
434 | 1.69M | pair); |
435 | 1.69M | gs_clean_fm_pair_attributes(dir, pair); |
436 | 1.69M | if (!xfont_only) { |
437 | 1.69M | int code; |
438 | | |
439 | | #ifdef DEBUG |
440 | | if (pair->num_chars != 0) { |
441 | | lprintf1("Error in gs_purge_fm_pair: num_chars =%d\n", |
442 | | pair->num_chars); |
443 | | } |
444 | | #endif |
445 | 1.69M | if (uid_is_XUID(&pair->UID)) { |
446 | 3.54k | gs_free_object(dir->memory->stable_memory, pair->UID.xvalues, "gs_purge_fm_pair"); |
447 | 3.54k | pair->UID.id = 0; |
448 | 3.54k | pair->UID.xvalues = NULL; |
449 | 3.54k | } |
450 | | |
451 | 1.69M | fm_pair_set_free(pair); |
452 | 1.69M | code = fm_pair_remove_from_list(dir, pair, &dir->fmcache.used); |
453 | 1.69M | if (code < 0) |
454 | 0 | return code; |
455 | 1.69M | code = fm_pair_insert_into_list(dir, pair, &dir->fmcache.free); |
456 | 1.69M | if (code < 0) |
457 | 0 | return code; |
458 | 1.69M | dir->fmcache.msize--; |
459 | 1.69M | } |
460 | 1.69M | return 0; |
461 | 1.69M | } |
462 | | |
463 | | |
464 | | /* ====== Character-level routines ====== */ |
465 | | |
466 | | /* |
467 | | * Allocate storage for caching a rendered character with possible |
468 | | * oversampling and/or alpha. Return the cached_char if OK, 0 if too big. |
469 | | * If the character is being oversampled, make the size decision |
470 | | * on the basis of the final (scaled-down) size. |
471 | | * |
472 | | * The iwidth and iheight parameters include scaling up for oversampling |
473 | | * (multiplication by 1 << pscale->{x,y}.) |
474 | | * The depth parameter is the final number of alpha bits; |
475 | | * depth <= x scale * y scale. |
476 | | */ |
477 | | int |
478 | | gx_alloc_char_bits(gs_font_dir * dir, gx_device_memory * pdev, |
479 | | ushort iwidth, ushort iheight, |
480 | | const gs_log2_scale_point * pscale, int depth, cached_char **pcc) |
481 | 6.73M | { |
482 | 6.73M | int log2_xscale = pscale->x; |
483 | 6.73M | int log2_yscale = pscale->y; |
484 | 6.73M | int log2_depth = ilog2(depth); |
485 | 6.73M | size_t nwidth_bits = ((size_t)iwidth >> log2_xscale) << log2_depth; |
486 | 6.73M | size_t isize, icdsize; |
487 | | #ifdef ENABLE_IMPOSSIBLE_ALPHA_CODE |
488 | | ulong isize2; |
489 | | #endif |
490 | 6.73M | size_t iraster; |
491 | 6.73M | cached_char *cc; |
492 | 6.73M | float HWResolution0 = 72, HWResolution1 = 72; /* default for dev == NULL */ |
493 | 6.73M | int code; |
494 | | |
495 | 6.73M | *pcc = 0; |
496 | 6.73M | HWResolution0 = pdev->HWResolution[0]; |
497 | 6.73M | HWResolution1 = pdev->HWResolution[1]; |
498 | | |
499 | | /* Compute the scaled-down bitmap size, and test against */ |
500 | | /* the maximum cachable character size. */ |
501 | | |
502 | 6.73M | iraster = bitmap_raster(nwidth_bits); |
503 | 6.73M | if (iraster != 0 && iheight >> log2_yscale > dir->ccache.upper / iraster) { |
504 | 577k | if_debug5m('k', pdev->memory, "[k]no cache bits: scale=%dx%d, raster/scale=%u, height/scale=%u, upper=%u\n", |
505 | 577k | 1 << log2_xscale, 1 << log2_yscale, |
506 | 577k | (unsigned int)iraster, iheight, dir->ccache.upper); |
507 | 577k | return 0; /* too big */ |
508 | 577k | } |
509 | | /* Compute the actual bitmap size(s) and allocate the bits. */ |
510 | 6.15M | { |
511 | | /* |
512 | | * Render to a full (possibly oversampled) bitmap; compress |
513 | | * (if needed) when done. |
514 | | * |
515 | | * HACK: Preserve the reference count and retained flag. |
516 | | */ |
517 | 6.15M | rc_header rc; |
518 | 6.15M | bool retained = pdev->retained; |
519 | 6.15M | gx_device *target = pdev->target; |
520 | | |
521 | 6.15M | rc = pdev->rc; |
522 | | /* Pass the correct target, but decrement its refct afterwards. */ |
523 | 6.15M | gs_make_mem_mono_device(pdev, pdev->memory, target); |
524 | 6.15M | rc_decrement_only(target, "gx_alloc_char_bits"); /* can't go to 0 */ |
525 | 6.15M | pdev->rc = rc; |
526 | 6.15M | pdev->retained = retained; |
527 | 6.15M | pdev->width = iwidth; |
528 | 6.15M | pdev->height = iheight; |
529 | 6.15M | pdev->raster = gx_device_raster((gx_device *)pdev, 1); |
530 | 6.15M | gdev_mem_bitmap_size(pdev, &isize); /* Assume less than max_ulong */ |
531 | 6.15M | pdev->HWResolution[0] = HWResolution0; |
532 | 6.15M | pdev->HWResolution[1] = HWResolution1; |
533 | 6.15M | } |
534 | 6.15M | icdsize = isize + sizeof_cached_char; |
535 | 6.15M | code = alloc_char(dir, icdsize, &cc); |
536 | 6.15M | if (code < 0) |
537 | 52 | return code; |
538 | 6.15M | *pcc = cc; |
539 | 6.15M | if (cc == 0) |
540 | 0 | return 0; |
541 | 6.15M | if_debug4m('k', pdev->memory, "[k]adding char "PRI_INTPTR":%u(%u,%u)\n", |
542 | 6.15M | (intptr_t)cc, (uint)icdsize, iwidth, iheight); |
543 | | |
544 | | /* Fill in the entry. */ |
545 | | |
546 | 6.15M | cc_set_depth(cc, depth); |
547 | 6.15M | cc->xglyph = gx_no_xglyph; |
548 | | /* Set the width and height to those of the device. */ |
549 | | /* Note that if we are oversampling without an alpha buffer. */ |
550 | | /* these are not the final unscaled dimensions. */ |
551 | 6.15M | cc->width = pdev->width; |
552 | 6.15M | cc->height = pdev->height; |
553 | 6.15M | cc->shift = 0; |
554 | 6.15M | cc_set_raster(cc, gdev_mem_raster(pdev)); |
555 | 6.15M | cc_set_pair_only(cc, 0); /* not linked in yet */ |
556 | 6.15M | cc->id = gx_no_bitmap_id; |
557 | 6.15M | cc->subpix_origin.x = cc->subpix_origin.y = 0; |
558 | 6.15M | cc->linked = false; |
559 | | |
560 | | /* Open the cache device(s). */ |
561 | 6.15M | gx_open_cache_device(pdev, cc); |
562 | | |
563 | 6.15M | return 0; |
564 | 6.15M | } |
565 | | |
566 | | /* Open the cache device. */ |
567 | | void |
568 | | gx_open_cache_device(gx_device_memory * dev, cached_char * cc) |
569 | 6.15M | { |
570 | 6.15M | byte *bits = cc_bits(cc); |
571 | 6.15M | size_t bsize; |
572 | | |
573 | 6.15M | gdev_mem_bitmap_size(dev, &bsize); |
574 | | |
575 | 6.15M | dev->width = cc->width; |
576 | 6.15M | dev->height = cc->height; |
577 | 6.15M | memset((char *)bits, 0, bsize); |
578 | 6.15M | dev->base = bits; |
579 | 6.15M | (*dev_proc(dev, open_device)) ((gx_device *) dev); /* initialize */ |
580 | 6.15M | } |
581 | | |
582 | | /* Remove a character from the cache. */ |
583 | | void |
584 | | gx_free_cached_char(gs_font_dir * dir, cached_char * cc) |
585 | 6.15M | { |
586 | 6.15M | char_cache_chunk *cck = cc->chunk; |
587 | | |
588 | 6.15M | dir->ccache.chunks = cck; |
589 | 6.15M | dir->ccache.cnext = (byte *) cc - cck->data; |
590 | 6.15M | if (cc->linked) |
591 | 6.15M | cc_pair(cc)->num_chars--; |
592 | 6.15M | if_debug2m('k', dir->memory, "[k]freeing char "PRI_INTPTR", pair="PRI_INTPTR"\n", |
593 | 6.15M | (intptr_t)cc, (intptr_t)cc_pair(cc)); |
594 | 6.15M | gx_bits_cache_free((gx_bits_cache *) & dir->ccache, &cc->head, cck); |
595 | 6.15M | } |
596 | | |
597 | | /* Add a character to the cache */ |
598 | | int |
599 | | gx_add_cached_char(gs_font_dir * dir, gx_device_memory * dev, |
600 | | cached_char * cc, cached_fm_pair * pair, const gs_log2_scale_point * pscale) |
601 | 6.15M | { |
602 | 6.15M | if_debug5m('k', dev->memory, |
603 | 6.15M | "[k]chaining char "PRI_INTPTR": pair="PRI_INTPTR", glyph=0x%lx, wmode=%d, depth=%d\n", |
604 | 6.15M | (intptr_t)cc, (intptr_t)pair, (ulong)cc->code, |
605 | 6.15M | cc->wmode, cc_depth(cc)); |
606 | 6.15M | if (dev != NULL) { |
607 | 6.15M | static const gs_log2_scale_point no_scale = |
608 | 6.15M | {0, 0}; |
609 | | |
610 | | /* Close the device, to flush the alpha buffer if any. */ |
611 | 6.15M | (*dev_proc(dev, close_device)) ((gx_device *) dev); |
612 | 6.15M | gx_add_char_bits(dir, cc, |
613 | 6.15M | (gs_device_is_abuf((gx_device *) dev) ? |
614 | 6.15M | &no_scale : pscale)); |
615 | 6.15M | } |
616 | | /* Add the new character to the hash table. */ |
617 | 6.15M | { |
618 | 6.15M | uint chi = chars_head_index(cc->code, pair); |
619 | | |
620 | 6.19M | while (dir->ccache.table[chi &= dir->ccache.table_mask] != 0) |
621 | 40.9k | chi++; |
622 | 6.15M | dir->ccache.table[chi] = cc; |
623 | 6.15M | if (cc->pair == NULL) { |
624 | | /* gx_show_text_retry could reset it when bbox_draw |
625 | | discovered an insufficient FontBBox and enlarged it. |
626 | | Glyph raster params could change then. */ |
627 | 0 | cc->pair = pair; |
628 | 6.15M | } else if (cc->pair != pair) { |
629 | | /* gx_add_fm_pair could drop the active font-matrix pair |
630 | | due to cache overflow during a charproc interpretation. |
631 | | Likely a single charproc renders too many characters |
632 | | for generating the character image. |
633 | | We have no mechanizm for locking font-matrix pairs in cache |
634 | | to avoud their dissipation. Therefore we consider this failure |
635 | | as an implementation limitation. */ |
636 | 0 | return_error(gs_error_invalidfont); |
637 | 0 | } |
638 | 6.15M | cc->linked = true; |
639 | 6.15M | cc_set_pair(cc, pair); |
640 | 6.15M | pair->num_chars++; |
641 | 6.15M | } |
642 | 0 | return 0; |
643 | 6.15M | } |
644 | | |
645 | | /* Adjust the bits of a newly-rendered character, by unscaling */ |
646 | | /* and compressing or converting to alpha values if necessary. */ |
647 | | void |
648 | | gx_add_char_bits(gs_font_dir * dir, cached_char * cc, |
649 | | const gs_log2_scale_point * plog2_scale) |
650 | 6.15M | { |
651 | 6.15M | int log2_x = plog2_scale->x, log2_y = plog2_scale->y; |
652 | 6.15M | uint raster = cc_raster(cc); |
653 | 6.15M | byte *bits = cc_bits(cc); |
654 | 6.15M | int depth = cc_depth(cc); |
655 | 6.15M | int log2_depth = ilog2(depth); |
656 | 6.15M | uint nwidth_bits, nraster; |
657 | 6.15M | gs_int_rect bbox; |
658 | | |
659 | | #ifdef DEBUG |
660 | | if (cc->width % (1 << log2_x) != 0 || |
661 | | cc->height % (1 << log2_y) != 0 |
662 | | ) { |
663 | | lprintf4("size %d,%d not multiple of scale %d,%d!\n", |
664 | | cc->width, cc->height, |
665 | | 1 << log2_x, 1 << log2_y); |
666 | | cc->width &= -1 << log2_x; |
667 | | cc->height &= -1 << log2_y; |
668 | | } |
669 | | #endif |
670 | | |
671 | | /* |
672 | | * Compute the bounding box before compressing. |
673 | | * We may have to scan more bits, but this is a lot faster than |
674 | | * compressing the white space. Note that all bbox values are |
675 | | * in bits, not pixels. |
676 | | */ |
677 | | |
678 | 6.15M | bits_bounding_box(bits, cc->height, raster, &bbox); |
679 | | |
680 | | /* |
681 | | * If the character was oversampled, compress it now. |
682 | | * In this case we know that log2_depth <= log2_x. |
683 | | * If the character was not oversampled, or if we converted |
684 | | * oversampling to alpha dynamically (using an alpha buffer |
685 | | * intermediate device), log2_x and log2_y are both zero, |
686 | | * but in the latter case we may still have depth > 1. |
687 | | */ |
688 | | |
689 | 6.15M | if ((log2_x | log2_y) != 0) { |
690 | 0 | if_debug5m('k', dir->memory, |
691 | 0 | "[k]compressing %dx%d by %dx%d to depth=%d\n", |
692 | 0 | cc->width, cc->height, 1 << log2_x, 1 << log2_y, |
693 | 0 | depth); |
694 | | #ifdef DEBUG |
695 | | if (gs_debug_c('K')) |
696 | | debug_dump_bitmap(dir->memory, bits, raster, cc->height, |
697 | | "[K]uncompressed bits"); |
698 | | #endif |
699 | | /* Truncate/round the bbox to a multiple of the scale. */ |
700 | 0 | { |
701 | 0 | int scale_x = 1 << log2_x; |
702 | |
|
703 | 0 | bbox.p.x &= -scale_x; |
704 | 0 | bbox.q.x = (bbox.q.x + scale_x - 1) & -scale_x; |
705 | 0 | } |
706 | 0 | { |
707 | 0 | int scale_y = 1 << log2_y; |
708 | |
|
709 | 0 | bbox.p.y &= -scale_y; |
710 | 0 | bbox.q.y = (bbox.q.y + scale_y - 1) & -scale_y; |
711 | 0 | } |
712 | 0 | cc->width = (bbox.q.x - bbox.p.x) >> log2_x; |
713 | 0 | cc->height = (bbox.q.y - bbox.p.y) >> log2_y; |
714 | 0 | nwidth_bits = cc->width << log2_depth; |
715 | 0 | nraster = bitmap_raster(nwidth_bits); |
716 | 0 | bits_compress_scaled(bits + raster * bbox.p.y, bbox.p.x, |
717 | 0 | cc->width << log2_x, |
718 | 0 | cc->height << log2_y, |
719 | 0 | raster, |
720 | 0 | bits, nraster, plog2_scale, log2_depth); |
721 | 0 | bbox.p.x >>= log2_x; |
722 | 0 | bbox.p.y >>= log2_y; |
723 | 6.15M | } else { |
724 | | /* No oversampling, just remove white space on all 4 sides. */ |
725 | 6.15M | const byte *from = bits + raster * bbox.p.y + (bbox.p.x >> 3); |
726 | | |
727 | 6.15M | cc->height = bbox.q.y - bbox.p.y; |
728 | 6.15M | bbox.p.x &= ~7; /* adjust to byte boundary */ |
729 | 6.15M | bbox.p.x >>= log2_depth; /* bits => pixels */ |
730 | 6.15M | bbox.q.x = (bbox.q.x + depth - 1) >> log2_depth; /* ditto */ |
731 | 6.15M | cc->width = bbox.q.x - bbox.p.x; |
732 | 6.15M | nwidth_bits = cc->width << log2_depth; |
733 | 6.15M | nraster = bitmap_raster(nwidth_bits); |
734 | 6.15M | if (bbox.p.x != 0 || nraster != raster) { |
735 | | /* Move the bits down and over. */ |
736 | 4.07M | byte *to = bits; |
737 | 4.07M | uint n = cc->height; |
738 | | |
739 | | /* We'd like to move only |
740 | | uint nbytes = (nwidth_bits + 7) >> 3; |
741 | | * bytes per scan line, but unfortunately this drops |
742 | | * the guaranteed zero padding at the end. |
743 | | */ |
744 | | |
745 | 72.5M | for (; n--; from += raster, to += nraster) |
746 | 68.5M | memmove(to, from, /*nbytes */ nraster); |
747 | 4.07M | } else if (bbox.p.y != 0) { /* Just move the bits down. */ |
748 | 2.07M | memmove(bits, from, raster * cc->height); |
749 | 2.07M | } |
750 | 6.15M | } |
751 | | |
752 | | /* Adjust the offsets to account for removed white space. */ |
753 | | |
754 | 6.15M | cc->offset.x -= int2fixed(bbox.p.x); |
755 | 6.15M | cc->offset.y -= int2fixed(bbox.p.y); |
756 | | |
757 | | /* Discard the memory device overhead that follows the bits, */ |
758 | | /* and any space reclaimed from unscaling or compression. */ |
759 | | |
760 | 6.15M | cc_set_raster(cc, nraster); |
761 | 6.15M | { |
762 | 6.15M | uint diff = ROUND_DOWN(cc->head.size - sizeof_cached_char - |
763 | 6.15M | nraster * cc->height, |
764 | 6.15M | align_cached_char_mod); |
765 | | |
766 | 6.15M | if (diff >= sizeof(cached_char_head)) { |
767 | 6.15M | shorten_cached_char(dir, cc, diff); |
768 | 6.15M | if_debug2m('K', dir->memory, "[K]shortening char "PRI_INTPTR" by %u (adding)\n", |
769 | 6.15M | (intptr_t)cc, diff); |
770 | 6.15M | } |
771 | 6.15M | } |
772 | | |
773 | | /* Assign a bitmap id. */ |
774 | | |
775 | 6.15M | cc->id = gs_next_ids(dir->memory, 1); |
776 | 6.15M | } |
777 | | |
778 | | /* Purge from the caches all references to a given font. */ |
779 | | static int |
780 | | gs_purge_font_from_char_caches_forced(gs_font * font, bool force) |
781 | 1.56M | { |
782 | 1.56M | gs_font_dir * dir; |
783 | 1.56M | cached_fm_pair *pair; |
784 | 1.56M | int count; |
785 | | |
786 | 1.56M | if (font->dir == NULL) |
787 | 0 | return 0; /* The font was not properly build due to errors. */ |
788 | 1.56M | if (!font->is_cached) |
789 | 873k | return 0; |
790 | 687k | dir = font->dir; |
791 | 687k | pair = dir->fmcache.mdata; |
792 | 687k | count = dir->fmcache.mmax; |
793 | 687k | font->is_cached = false; /* Prevent redundant execution. */ |
794 | 687k | if_debug1m('k', font->memory, "[k]purging font "PRI_INTPTR"\n", |
795 | 687k | (intptr_t)font); |
796 | 138M | for (; count--; pair++) { |
797 | 137M | if (pair->font == font) { |
798 | 888k | if (!force && uid_is_valid(&pair->UID)) { /* Keep the entry. */ |
799 | 109k | gs_clean_fm_pair(dir, pair); |
800 | 778k | } else { |
801 | 778k | int code = gs_purge_fm_pair(dir, pair, 0); |
802 | | |
803 | 778k | if (code < 0) |
804 | 0 | return code; |
805 | 778k | } |
806 | 888k | } |
807 | 137M | } |
808 | 687k | return 0; |
809 | 687k | } |
810 | | |
811 | | /* Purge from the caches all references to a given font, |
812 | | with leaving persistent chars in the cache. */ |
813 | | int |
814 | | gs_purge_font_from_char_caches(gs_font * font) |
815 | 1.53M | { |
816 | | /* This function is called when a font is being released. |
817 | | The purpose is to remove all cache attributes, |
818 | | which may point to the font data. |
819 | | Note : when a font has a valid XUID, |
820 | | it doesn't release cache entries and cached chars, |
821 | | so that they may be used in future |
822 | | if a font with same XUID appears again. |
823 | | All this improves the performance when |
824 | | a document executes a sequence like this : |
825 | | |
826 | | n { |
827 | | save /fontname findfont 10 scalefont |
828 | | (xyz) show |
829 | | restore |
830 | | } repeat |
831 | | */ |
832 | 1.53M | return gs_purge_font_from_char_caches_forced(font, false); |
833 | 1.53M | } |
834 | | |
835 | | /* Purge from the caches all references to a given font, |
836 | | without leaving persistent chars in the cache. */ |
837 | | int |
838 | | gs_purge_font_from_char_caches_completely(gs_font * font) |
839 | 29.2k | { |
840 | | /* A client should call this finction |
841 | | when it frees a font, |
842 | | and the client doesn't need to leave |
843 | | persistent cache entries for this font |
844 | | even if the font has a valid XUID. |
845 | | */ |
846 | 29.2k | return gs_purge_font_from_char_caches_forced(font, true); |
847 | 29.2k | } |
848 | | |
849 | | /* ------ Internal routines ------ */ |
850 | | |
851 | | /* Allocate data space for a cached character, adding a new chunk if needed. */ |
852 | | static int |
853 | | alloc_char(gs_font_dir * dir, ulong icdsize, cached_char **pcc) |
854 | 6.15M | { /* Try allocating at the current position first. */ |
855 | 6.15M | cached_char *cc; |
856 | 6.15M | int code = alloc_char_in_chunk(dir, icdsize, &cc); |
857 | | |
858 | 6.15M | *pcc = cc; |
859 | 6.15M | if (code < 0) |
860 | 52 | return code; |
861 | 6.15M | if (cc == 0) { |
862 | 36.0k | if (dir->ccache.bspace < dir->ccache.bmax) { /* Allocate another chunk. */ |
863 | 35.9k | gs_memory_t *mem = dir->ccache.bits_memory; |
864 | 35.9k | char_cache_chunk *cck_prev = dir->ccache.chunks; |
865 | 35.9k | char_cache_chunk *cck; |
866 | 35.9k | uint cksize = ROUND_UP(dir->ccache.bmax / 5 + 1, obj_align_mod); |
867 | 35.9k | uint tsize = ROUND_UP(dir->ccache.bmax - dir->ccache.bspace, obj_align_mod); |
868 | 35.9k | byte *cdata; |
869 | | |
870 | 35.9k | if (cksize > tsize) |
871 | 86 | cksize = tsize; |
872 | 35.9k | if (icdsize + sizeof(cached_char_head) > cksize) { |
873 | 0 | if_debug2m('k', mem, |
874 | 0 | "[k]no cache bits: cdsize+head=%lu, cksize=%u\n", |
875 | 0 | icdsize + sizeof(cached_char_head), |
876 | 0 | cksize); |
877 | 0 | return 0; /* wouldn't fit */ |
878 | 0 | } |
879 | 35.9k | cck = (char_cache_chunk *) |
880 | 35.9k | gs_alloc_bytes_immovable(mem, sizeof(*cck), |
881 | 35.9k | "char cache chunk"); |
882 | 35.9k | if (cck == 0) |
883 | 0 | return 0; |
884 | 35.9k | cdata = |
885 | 35.9k | gs_alloc_struct_array_immovable(mem, cksize, byte, |
886 | 35.9k | &st_font_cache_bytes, |
887 | 35.9k | "char cache chunk(data)"); |
888 | 35.9k | if (cdata == 0) { |
889 | 0 | gs_free_object(mem, cck, "char cache chunk"); |
890 | 0 | return 0; |
891 | 0 | } |
892 | 35.9k | gx_bits_cache_chunk_init(cck, cdata, cksize); |
893 | 35.9k | cck->next = cck_prev->next; |
894 | 35.9k | cck_prev->next = cck; |
895 | 35.9k | dir->ccache.bspace += cksize; |
896 | 35.9k | dir->ccache.chunks = cck; |
897 | 35.9k | } else { /* Cycle through existing chunks. */ |
898 | 149 | char_cache_chunk *cck_init = dir->ccache.chunks; |
899 | 149 | char_cache_chunk *cck = cck_init; |
900 | | |
901 | 178 | while ((dir->ccache.chunks = cck = cck->next) != cck_init) { |
902 | 176 | dir->ccache.cnext = 0; |
903 | 176 | code = alloc_char_in_chunk(dir, icdsize, &cc); |
904 | 176 | if (code < 0) |
905 | 0 | return code; |
906 | 176 | if (cc != 0) { |
907 | 147 | *pcc = cc; |
908 | 147 | return 0; |
909 | 147 | } |
910 | 176 | } |
911 | 149 | } |
912 | 35.9k | dir->ccache.cnext = 0; |
913 | 35.9k | code = alloc_char_in_chunk(dir, icdsize, &cc); |
914 | 35.9k | if (code < 0) |
915 | 0 | return code; |
916 | 35.9k | *pcc = cc; |
917 | 35.9k | } |
918 | 6.15M | return 0; |
919 | 6.15M | } |
920 | | |
921 | | /* Allocate a character in the current chunk. */ |
922 | | static int |
923 | | alloc_char_in_chunk(gs_font_dir * dir, ulong icdsize, cached_char **pcc) |
924 | 6.19M | { |
925 | 6.19M | char_cache_chunk *cck = dir->ccache.chunks; |
926 | 6.19M | cached_char_head *cch; |
927 | | |
928 | 25.7M | #define cc ((cached_char *)cch) |
929 | | |
930 | 6.19M | *pcc = 0; |
931 | 6.30M | while (gx_bits_cache_alloc((gx_bits_cache *) & dir->ccache, |
932 | 6.30M | icdsize, &cch) < 0 |
933 | 6.19M | ) { |
934 | 147k | if (cch == 0) { /* Not enough room to allocate in this chunk. */ |
935 | 36.1k | return 0; |
936 | 36.1k | } |
937 | 111k | else { /* Free the character */ |
938 | 111k | cached_fm_pair *pair = cc_pair(cc); |
939 | | |
940 | 111k | if (pair != 0) { |
941 | 111k | uint chi = chars_head_index(cc->code, pair); |
942 | 111k | uint cnt = dir->ccache.table_mask + 1; |
943 | | |
944 | 965k | while (dir->ccache.table[chi & dir->ccache.table_mask] != cc) { |
945 | 853k | chi++; |
946 | 853k | if (cnt-- == 0) |
947 | 52 | return_error(gs_error_unregistered); /* Must not happen. */ |
948 | 853k | } |
949 | 111k | hash_remove_cached_char(dir, chi); |
950 | 111k | } |
951 | | |
952 | 111k | gx_free_cached_char(dir, cc); |
953 | 111k | } |
954 | 147k | } |
955 | | |
956 | 6.15M | cc->chunk = cck; |
957 | 6.15M | cc->loc = (byte *) cc - cck->data; |
958 | 6.15M | *pcc = cc; |
959 | 6.15M | return 0; |
960 | | |
961 | 6.19M | #undef cc |
962 | 6.19M | } |
963 | | |
964 | | /* Remove the cached_char at a given index in the hash table. */ |
965 | | /* In order not to slow down lookup, we relocate following entries. */ |
966 | | static void |
967 | | hash_remove_cached_char(gs_font_dir * dir, uint chi) |
968 | 6.15M | { |
969 | 6.15M | uint mask = dir->ccache.table_mask; |
970 | 6.15M | uint from = ((chi &= mask) + 1) & mask; |
971 | 6.15M | cached_char *cc; |
972 | | |
973 | 6.15M | dir->ccache.table[chi] = 0; |
974 | 6.23M | while ((cc = dir->ccache.table[from]) != 0) { /* Loop invariants: chars[chi] == 0; */ |
975 | | /* chars[chi+1..from] != 0. */ |
976 | 84.0k | uint fchi = chars_head_index(cc->code, cc_pair(cc)); |
977 | | |
978 | | /* If chi <= fchi < from, we relocate the character. */ |
979 | | /* Note that '<=' must take wraparound into account. */ |
980 | 84.0k | if ((chi < from ? chi <= fchi && fchi < from : |
981 | 84.0k | chi <= fchi || fchi < from) |
982 | 84.0k | ) { |
983 | 425 | dir->ccache.table[chi] = cc; |
984 | 425 | dir->ccache.table[from] = 0; |
985 | 425 | chi = from; |
986 | 425 | } |
987 | 84.0k | from = (from + 1) & mask; |
988 | 84.0k | } |
989 | 6.15M | } |
990 | | |
991 | | /* Shorten a cached character. */ |
992 | | /* diff >= sizeof(cached_char_head). */ |
993 | | static void |
994 | | shorten_cached_char(gs_font_dir * dir, cached_char * cc, uint diff) |
995 | 6.15M | { |
996 | 6.15M | gx_bits_cache_shorten((gx_bits_cache *) & dir->ccache, &cc->head, |
997 | 6.15M | diff, cc->chunk); |
998 | 6.15M | if_debug2m('K', dir->memory, "[K]shortening creates free block "PRI_INTPTR"(%u)\n", |
999 | 6.15M | (intptr_t)((byte *) cc + cc->head.size), diff); |
1000 | 6.15M | } |