/src/ghostpdl/base/gsgcache.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 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 | | /* Glyph data cache methods. */ |
18 | | |
19 | | #include "gx.h" |
20 | | #include "gserrors.h" |
21 | | #include "memory_.h" |
22 | | #include "gsstruct.h" |
23 | | #include "gsgdata.h" |
24 | | #include "gsgcache.h" |
25 | | #include "gxfont.h" |
26 | | #include "gxfont42.h" |
27 | | |
28 | | /* |
29 | | * This implementation hardcodes the type 42 font type. |
30 | | * We could generalize it, but since CIDFontType 0 uses |
31 | | * a PS procedure for reading glyphs, it is hardly applicable. |
32 | | * |
33 | | * The caching is mostly useful for glyphs with multiple components, |
34 | | * but CIDFontType 0 has 2 components max, which are relatively seldom. |
35 | | * Also it is low useful for fonts, which fully loaded into RAM. |
36 | | * FAPI does not need a caching, because renderer pludins access |
37 | | * font data through a file handle by own means. |
38 | | * |
39 | | * Due to all above, currently the caching is applied |
40 | | * only while emulating CIDFontType 2 with a True Type file. |
41 | | */ |
42 | | |
43 | | typedef struct gs_glyph_cache_elem_s gs_glyph_cache_elem; |
44 | | struct gs_glyph_cache_elem_s { |
45 | | gs_glyph_data_t gd; |
46 | | uint glyph_index; |
47 | | uint lock_count; |
48 | | gs_glyph_cache_elem *next; |
49 | | }; |
50 | | gs_private_st_composite(st_glyph_cache_elem, gs_glyph_cache_elem, "gs_glyph_cache_elem", |
51 | | gs_glyph_cache_elem_enum_ptrs, gs_glyph_cache_elem_reloc_ptrs); |
52 | | |
53 | | static |
54 | 0 | ENUM_PTRS_WITH(gs_glyph_cache_elem_enum_ptrs, gs_glyph_cache_elem *e) |
55 | 0 | { |
56 | 0 | index --; |
57 | 0 | if (index < ST_GLYPH_DATA_NUM_PTRS) |
58 | 0 | return ENUM_USING(st_glyph_data, &e->gd, sizeof(e->gd), index); |
59 | 0 | return 0; |
60 | 0 | } |
61 | 0 | ENUM_PTR(0, gs_glyph_cache_elem, next); |
62 | 0 | ENUM_PTRS_END |
63 | 0 | static RELOC_PTRS_WITH(gs_glyph_cache_elem_reloc_ptrs, gs_glyph_cache_elem *e) |
64 | 0 | { |
65 | 0 | RELOC_PTR(gs_glyph_cache_elem, next); |
66 | 0 | RELOC_USING(st_glyph_data, &e->gd, sizeof(e->gd)); |
67 | 0 | } RELOC_PTRS_END |
68 | | |
69 | | struct gs_glyph_cache_s { |
70 | | int total_size; |
71 | | gs_glyph_cache_elem *list; |
72 | | gs_memory_t *memory; |
73 | | gs_font_type42 *pfont; |
74 | | stream *s; |
75 | | get_glyph_data_from_file read_data; |
76 | | }; |
77 | | gs_private_st_ptrs4(st_glyph_cache, gs_glyph_cache, "gs_glyph_cache", |
78 | | gs_glyph_cache_enum_ptrs, gs_glyph_cache_reloc_ptrs, list, memory, pfont, s); |
79 | | |
80 | | GS_NOTIFY_PROC(gs_glpyh_cache__release); |
81 | | |
82 | | gs_glyph_cache * |
83 | | gs_glyph_cache__alloc(gs_font_type42 *pfont, stream *s, |
84 | | get_glyph_data_from_file read_data) |
85 | 0 | { |
86 | 0 | int code; |
87 | 0 | gs_memory_t *mem = pfont->memory->stable_memory; |
88 | 0 | gs_glyph_cache *gdcache = (gs_glyph_cache *)gs_alloc_struct(mem, |
89 | 0 | gs_glyph_cache, &st_glyph_cache, "gs_glyph_cache"); |
90 | 0 | if (gdcache == 0) |
91 | 0 | return 0; |
92 | 0 | gdcache->total_size = 0; |
93 | 0 | gdcache->list = NULL; |
94 | 0 | gdcache->pfont = pfont; |
95 | 0 | gdcache->s = s; |
96 | | /* |
97 | | * The cache elements need to be in stable memory so they don't |
98 | | * get removed by 'restore' (elements can be created at a different |
99 | | * save level than the current level) |
100 | | */ |
101 | 0 | gdcache->memory = mem; |
102 | 0 | gdcache->read_data = read_data; |
103 | 0 | code = gs_font_notify_register((gs_font *)pfont, gs_glyph_cache__release, (void *)gdcache); |
104 | 0 | if (code < 0) { |
105 | 0 | gs_free_object(mem, gdcache, "gs_glyph_cache__alloc"); |
106 | 0 | gdcache = NULL; |
107 | 0 | } |
108 | 0 | return gdcache; |
109 | 0 | } |
110 | | |
111 | | int |
112 | | gs_glyph_cache__release(void *data, void *event) |
113 | 0 | { |
114 | 0 | gs_glyph_cache *self = (gs_glyph_cache *)data; |
115 | 0 | gs_glyph_cache_elem *e = self->list; |
116 | 0 | gs_font_type42 *pfont = self->pfont; |
117 | |
|
118 | 0 | while (e != NULL) { |
119 | 0 | gs_glyph_cache_elem *next_e; |
120 | |
|
121 | 0 | next_e = e->next; |
122 | 0 | e->gd.procs->free(&e->gd, "gs_glyph_cache__release"); |
123 | 0 | gs_free_object(self->memory, e, "gs_glyph_cache_elem__release"); |
124 | 0 | e = next_e; |
125 | 0 | } |
126 | 0 | self->list = NULL; |
127 | 0 | gs_font_notify_unregister((gs_font *)pfont, gs_glyph_cache__release, (void *)self); |
128 | 0 | gs_free_object(self->memory, self, "gs_glyph_cache__release"); |
129 | 0 | return 0; |
130 | 0 | } |
131 | | |
132 | | static gs_glyph_cache_elem ** |
133 | | gs_glyph_cache_elem__locate(gs_glyph_cache *self, uint glyph_index) |
134 | 0 | { /* If not fond, returns an unlocked element. */ |
135 | 0 | gs_glyph_cache_elem **e = &self->list, **p_unlocked = NULL; |
136 | 0 | int count = 0; /* debug purpose only */ |
137 | |
|
138 | 0 | for (; *e != 0; e = &(*e)->next, count++) { |
139 | 0 | if ((*e)->glyph_index == glyph_index) { |
140 | 0 | return e; |
141 | 0 | } |
142 | 0 | if ((*e)->lock_count == 0) |
143 | 0 | p_unlocked = e; |
144 | 0 | } |
145 | 0 | return p_unlocked; |
146 | 0 | } |
147 | | |
148 | | static inline void |
149 | | gs_glyph_cache_elem__move_to_head(gs_glyph_cache *self, gs_glyph_cache_elem **pe) |
150 | 0 | { gs_glyph_cache_elem *e = *pe; |
151 | |
|
152 | 0 | *pe = e->next; |
153 | 0 | e->next = self->list; |
154 | 0 | self->list = e; |
155 | 0 | } |
156 | | |
157 | | /* Manage the glyph data using the font's allocator. */ |
158 | | static void |
159 | | gs_glyph_cache_elem__free_data(gs_glyph_data_t *pgd, client_name_t cname) |
160 | 0 | { gs_glyph_cache_elem *e = (gs_glyph_cache_elem *)pgd->proc_data; |
161 | |
|
162 | 0 | e->lock_count--; |
163 | 0 | } |
164 | | static int |
165 | | gs_glyph_cache_elem__substring(gs_glyph_data_t *pgd, uint offset, uint size) |
166 | 0 | { gs_glyph_cache_elem *e = (gs_glyph_cache_elem *)pgd->proc_data; |
167 | |
|
168 | 0 | e->lock_count++; |
169 | 0 | return_error(gs_error_unregistered); /* Unsupported; should not happen. */ |
170 | 0 | } |
171 | | |
172 | | static const gs_glyph_data_procs_t gs_glyph_cache_elem_procs = { |
173 | | gs_glyph_cache_elem__free_data, gs_glyph_cache_elem__substring |
174 | | }; |
175 | | |
176 | | int |
177 | | gs_get_glyph_data_cached(gs_font_type42 *pfont, uint glyph_index, gs_glyph_data_t *pgd) |
178 | 0 | { gs_glyph_cache *gdcache = pfont->data.gdcache; |
179 | 0 | gs_glyph_cache_elem **pe = gs_glyph_cache_elem__locate(gdcache, glyph_index); |
180 | 0 | gs_glyph_cache_elem *e = NULL; |
181 | |
|
182 | 0 | if (pe == NULL || (*pe)->glyph_index != glyph_index) { |
183 | 0 | int code; |
184 | |
|
185 | 0 | if (pe != NULL && gdcache->total_size > 32767 /* arbitrary */ && |
186 | 0 | (*pe)->lock_count <= 0) { |
187 | | /* Release the element's data, and move it : */ |
188 | 0 | e = *pe; |
189 | 0 | gdcache->total_size -= e->gd.bits.size + sizeof(*e); |
190 | 0 | e->gd.procs->free(&e->gd, "gs_get_glyph_data_cached"); |
191 | 0 | gs_glyph_cache_elem__move_to_head(gdcache, pe); |
192 | 0 | } else { |
193 | | /* Allocate new head element. */ |
194 | 0 | e = (gs_glyph_cache_elem *)gs_alloc_struct(gdcache->memory, |
195 | 0 | gs_glyph_cache_elem, &st_glyph_cache_elem, "gs_glyph_cache_elem"); |
196 | 0 | if (e == NULL) |
197 | 0 | return_error(gs_error_VMerror); |
198 | 0 | memset(e, 0, sizeof(*e)); |
199 | 0 | e->next = gdcache->list; |
200 | 0 | gdcache->list = e; |
201 | 0 | e->gd.memory = gdcache->memory; |
202 | 0 | } |
203 | | /* Load the element's data : */ |
204 | 0 | code = (*gdcache->read_data)(pfont, gdcache->s, glyph_index, &e->gd); |
205 | 0 | if (code < 0) |
206 | 0 | return code; |
207 | 0 | gdcache->total_size += e->gd.bits.size + sizeof(*e); |
208 | 0 | e->glyph_index = glyph_index; |
209 | 0 | } else { |
210 | | /* Move the element : */ |
211 | 0 | e = *pe; |
212 | 0 | gs_glyph_cache_elem__move_to_head(gdcache, pe); |
213 | 0 | } |
214 | | /* Copy data and set procs : */ |
215 | 0 | pgd->bits = e->gd.bits; |
216 | 0 | pgd->proc_data = e; |
217 | 0 | pgd->procs = &gs_glyph_cache_elem_procs; |
218 | 0 | e->lock_count++; |
219 | 0 | return 0; |
220 | 0 | } |