/src/ghostpdl/base/gsicc_profilecache.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2022 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., 1305 Grant Avenue - Suite 200, Novato, |
13 | | CA 94945, U.S.A., +1(415)492-9861, for further information. |
14 | | */ |
15 | | |
16 | | /* A cache for icc colorspaces that were created from PS CIE color |
17 | | spaces or from PDF cal color spaces. |
18 | | */ |
19 | | |
20 | | #include "std.h" |
21 | | #include "stdpre.h" |
22 | | #include "gstypes.h" |
23 | | #include "gsmemory.h" |
24 | | #include "gsstruct.h" |
25 | | #include "scommon.h" |
26 | | #include "gx.h" |
27 | | #include "gzstate.h" |
28 | | #include "gscms.h" |
29 | | #include "gsicc_profilecache.h" |
30 | | #include "gserrors.h" |
31 | | #include "assert_.h" |
32 | | |
33 | 14.2k | #define ICC_CACHE_MAXPROFILE 50 |
34 | | |
35 | | /* Static prototypes */ |
36 | | static void rc_gsicc_profile_cache_free(gs_memory_t * mem, void *ptr_in, |
37 | | client_name_t cname); |
38 | | static void gsicc_remove_cs_entry(gsicc_profile_cache_t *profile_cache); |
39 | | |
40 | | gs_private_st_ptrs2(st_profile_entry, gsicc_profile_entry_t, |
41 | | "gsicc_profile_entry", profile_entry_enum_ptrs, |
42 | | profile_entry_reloc_ptrs, color_space, next); |
43 | | gs_private_st_ptrs1(st_profile_cache, gsicc_profile_cache_t, |
44 | | "gsicc_profile_cache", profile_list_enum_ptrs, |
45 | | profile_list_reloc_ptrs, head); |
46 | | |
47 | | /** |
48 | | * gsicc_cache_new: Allocate a new ICC cache manager |
49 | | * Return value: Pointer to allocated manager, or NULL on failure. |
50 | | **/ |
51 | | gsicc_profile_cache_t * |
52 | | gsicc_profilecache_new(gs_memory_t *memory) |
53 | 821k | { |
54 | 821k | gsicc_profile_cache_t *result; |
55 | | |
56 | | /* We want this to be maintained in stable_memory. It should not be effected by the |
57 | | save and restores */ |
58 | 821k | memory = memory->stable_memory; |
59 | 821k | result = gs_alloc_struct(memory, gsicc_profile_cache_t, |
60 | 821k | &st_profile_cache, "gsicc_profilecache_new"); |
61 | 821k | if ( result == NULL ) |
62 | 0 | return(NULL); |
63 | 821k | rc_init_free(result, memory, 1, rc_gsicc_profile_cache_free); |
64 | 821k | result->head = NULL; |
65 | 821k | result->num_entries = 0; |
66 | 821k | result->memory = memory; |
67 | 821k | return(result); |
68 | 821k | } |
69 | | |
70 | | static void |
71 | | rc_gsicc_profile_cache_free(gs_memory_t * mem, void *ptr_in, client_name_t cname) |
72 | 821k | { |
73 | 821k | gsicc_profile_cache_t *profile_cache = (gsicc_profile_cache_t * ) ptr_in; |
74 | 821k | gsicc_profile_entry_t *curr = profile_cache->head, *next; |
75 | | |
76 | 821k | assert(mem->stable_memory == profile_cache->memory); |
77 | 835k | while (curr != NULL ){ |
78 | 14.2k | next = curr->next; |
79 | 14.2k | rc_decrement(curr->color_space, "rc_gsicc_profile_cache_free"); |
80 | 14.2k | gs_free_object(profile_cache->memory, curr, |
81 | 14.2k | "rc_gsicc_profile_cache_free"); |
82 | 14.2k | profile_cache->num_entries--; |
83 | 14.2k | curr = next; |
84 | 14.2k | } |
85 | | #ifdef DEBUG |
86 | | if (profile_cache->num_entries != 0) |
87 | | emprintf1(mem,"gsicc_profile_cache_free, num_entries is %d (should be 0).\n", |
88 | | profile_cache->num_entries); |
89 | | #endif |
90 | 821k | gs_free_object(profile_cache->memory, profile_cache, |
91 | 821k | "rc_gsicc_profile_cache_free"); |
92 | 821k | } |
93 | | |
94 | | void |
95 | | gsicc_add_cs(gs_gstate * pgs, gs_color_space * colorspace, uint64_t dictkey) |
96 | 14.3k | { |
97 | 14.3k | gsicc_profile_entry_t *result; |
98 | 14.3k | gsicc_profile_cache_t *profile_cache = pgs->icc_profile_cache; |
99 | 14.3k | gs_memory_t *memory = profile_cache->memory; |
100 | | |
101 | 14.3k | if (dictkey == 0) |
102 | 72 | return; |
103 | | |
104 | | /* The entry has to be added in stable memory. We want them |
105 | | to be maintained across the gsave and grestore process */ |
106 | 14.2k | result = gs_alloc_struct(memory, gsicc_profile_entry_t, |
107 | 14.2k | &st_profile_entry, "gsicc_add_cs"); |
108 | 14.2k | if (result == NULL) |
109 | 0 | return; /* FIXME */ |
110 | | |
111 | | /* If needed, remove an entry (the last one) */ |
112 | 14.2k | if (profile_cache->num_entries >= ICC_CACHE_MAXPROFILE) { |
113 | 0 | gsicc_remove_cs_entry(profile_cache); |
114 | 0 | } |
115 | | /* Add to the top of the list. That way we find the MRU enty right away. |
116 | | Last entry stays the same. */ |
117 | 14.2k | result->next = profile_cache->head; |
118 | 14.2k | profile_cache->head = result; /* MRU */ |
119 | 14.2k | result->color_space = colorspace; |
120 | 14.2k | rc_increment(colorspace); |
121 | 14.2k | result->key = dictkey; |
122 | 14.2k | if_debug2m(gs_debug_flag_icc, memory, |
123 | 14.2k | "[icc] Add cs to cache = "PRI_INTPTR", hash = %"PRIu64"\n", |
124 | 14.2k | (intptr_t)result->color_space, (uint64_t)result->key); |
125 | 14.2k | profile_cache->num_entries++; |
126 | 14.2k | } |
127 | | |
128 | | gs_color_space* |
129 | | gsicc_find_cs(uint64_t key_test, gs_gstate * pgs) |
130 | 76.6k | { |
131 | 76.6k | gsicc_profile_cache_t *profile_cache = pgs->icc_profile_cache; |
132 | 76.6k | gsicc_profile_entry_t *prev = NULL, *curr = profile_cache->head; |
133 | | |
134 | 76.6k | if (key_test == 0) |
135 | 72 | return NULL; |
136 | | |
137 | | /* Look through the cache for the key. If found, move to MRU */ |
138 | 109k | while (curr != NULL ){ |
139 | 90.0k | if (curr->key == key_test){ |
140 | 56.7k | if_debug2m(gs_debug_flag_icc, pgs->memory, |
141 | 56.7k | "[icc] Found cs = "PRI_INTPTR", hash = %"PRIu64"\n", |
142 | 56.7k | (intptr_t)curr->color_space, (uint64_t)curr->key); |
143 | | /* If not already at head of list, move this one there */ |
144 | 56.7k | if (curr != profile_cache->head) { |
145 | | /* We need to move found one to the top of the list. */ |
146 | 13.2k | prev->next = curr->next; |
147 | 13.2k | curr->next = profile_cache->head; |
148 | 13.2k | profile_cache->head = curr; |
149 | 13.2k | } |
150 | 56.7k | return(curr->color_space); |
151 | 56.7k | } |
152 | 33.2k | prev = curr; |
153 | 33.2k | curr = curr->next; |
154 | 33.2k | } |
155 | 19.8k | return(NULL); |
156 | 76.5k | } |
157 | | |
158 | | /* Remove the LRU entry, which ideally is at the bottom. Note that there |
159 | | is no need to have a ref_count in this structure since the color |
160 | | space objects that are the member variables are reference counted themselves */ |
161 | | static void |
162 | | gsicc_remove_cs_entry(gsicc_profile_cache_t *profile_cache) |
163 | 0 | { |
164 | 0 | gs_memory_t *memory = profile_cache->memory; |
165 | 0 | gsicc_profile_entry_t *prev = NULL, *curr = profile_cache->head; |
166 | |
|
167 | | #ifdef DEBUG |
168 | | if (curr == NULL) { |
169 | | emprintf(memory, " attempt to remove from an empty profile cache.\n"); |
170 | | return; /* gs_abort(); */ |
171 | | } |
172 | | #endif |
173 | 0 | while (curr->next != NULL) { |
174 | 0 | prev = curr; |
175 | 0 | curr = curr->next; |
176 | 0 | } |
177 | 0 | profile_cache->num_entries--; |
178 | 0 | if (prev == NULL) { |
179 | | /* No more entries */ |
180 | 0 | profile_cache->head = NULL; |
181 | | #ifdef DEBUG |
182 | | if (profile_cache->num_entries != 0) { |
183 | | emprintf1(memory, "profile cache list empty, but list has num_entries=%d.\n", |
184 | | profile_cache->num_entries); |
185 | | } |
186 | | #endif |
187 | 0 | } else { |
188 | 0 | prev->next = NULL; /* new tail */ |
189 | 0 | } |
190 | | /* Decremented, but someone could still be referencing this */ |
191 | | /* If found again in the source document, it will be regenerated |
192 | | and added back into the cache. */ |
193 | 0 | if_debug2m(gs_debug_flag_icc, memory, |
194 | 0 | "[icc] Remove cs from cache = "PRI_INTPTR", hash = %"PRIu64"\n", |
195 | 0 | (intptr_t)curr->color_space, (uint64_t)curr->key); |
196 | 0 | rc_decrement(curr->color_space, "gsicc_remove_cs_entry"); |
197 | 0 | gs_free_object(memory, curr, "gsicc_remove_cs_entry"); |
198 | 0 | } |