/src/ghostpdl/base/gsicc_profilecache.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 | | /* 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 | 21.0k | #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 | 3.35M | { |
54 | 3.35M | 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 | 3.35M | memory = memory->stable_memory; |
59 | 3.35M | result = gs_alloc_struct(memory, gsicc_profile_cache_t, |
60 | 3.35M | &st_profile_cache, "gsicc_profilecache_new"); |
61 | 3.35M | if ( result == NULL ) |
62 | 0 | return(NULL); |
63 | 3.35M | rc_init_free(result, memory, 1, rc_gsicc_profile_cache_free); |
64 | 3.35M | result->head = NULL; |
65 | 3.35M | result->num_entries = 0; |
66 | 3.35M | result->memory = memory; |
67 | 3.35M | return(result); |
68 | 3.35M | } |
69 | | |
70 | | static void |
71 | | rc_gsicc_profile_cache_free(gs_memory_t * mem, void *ptr_in, client_name_t cname) |
72 | 3.35M | { |
73 | 3.35M | gsicc_profile_cache_t *profile_cache = (gsicc_profile_cache_t * ) ptr_in; |
74 | 3.35M | gsicc_profile_entry_t *curr = profile_cache->head, *next; |
75 | | |
76 | 3.35M | assert(mem->stable_memory == profile_cache->memory); |
77 | 3.37M | while (curr != NULL ){ |
78 | 21.0k | next = curr->next; |
79 | 21.0k | rc_decrement(curr->color_space, "rc_gsicc_profile_cache_free"); |
80 | 21.0k | gs_free_object(profile_cache->memory, curr, |
81 | 21.0k | "rc_gsicc_profile_cache_free"); |
82 | 21.0k | profile_cache->num_entries--; |
83 | 21.0k | curr = next; |
84 | 21.0k | } |
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 | 3.35M | gs_free_object(profile_cache->memory, profile_cache, |
91 | 3.35M | "rc_gsicc_profile_cache_free"); |
92 | 3.35M | } |
93 | | |
94 | | int |
95 | | gsicc_add_cs(gs_gstate * pgs, gs_color_space * colorspace, uint64_t dictkey) |
96 | 21.3k | { |
97 | 21.3k | gsicc_profile_entry_t *result; |
98 | 21.3k | gsicc_profile_cache_t *profile_cache = pgs->icc_profile_cache; |
99 | 21.3k | gs_memory_t *memory = profile_cache->memory; |
100 | | |
101 | 21.3k | if (dictkey == 0) |
102 | 228 | return 0; |
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 | 21.0k | result = gs_alloc_struct(memory, gsicc_profile_entry_t, |
107 | 21.0k | &st_profile_entry, "gsicc_add_cs"); |
108 | 21.0k | if (result == NULL) |
109 | 0 | return_error(gs_error_VMerror); |
110 | | |
111 | | /* If needed, remove an entry (the last one) */ |
112 | 21.0k | 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 | 21.0k | result->next = profile_cache->head; |
118 | 21.0k | profile_cache->head = result; /* MRU */ |
119 | 21.0k | result->color_space = colorspace; |
120 | 21.0k | rc_increment(colorspace); |
121 | 21.0k | result->key = dictkey; |
122 | 21.0k | if_debug2m(gs_debug_flag_icc, memory, |
123 | 21.0k | "[icc] Add cs to cache = "PRI_INTPTR", hash = %"PRIu64"\n", |
124 | 21.0k | (intptr_t)result->color_space, (uint64_t)result->key); |
125 | 21.0k | profile_cache->num_entries++; |
126 | 21.0k | return 0; |
127 | 21.0k | } |
128 | | |
129 | | gs_color_space* |
130 | | gsicc_find_cs(uint64_t key_test, gs_gstate * pgs) |
131 | 195k | { |
132 | 195k | gsicc_profile_cache_t *profile_cache = pgs->icc_profile_cache; |
133 | 195k | gsicc_profile_entry_t *prev = NULL, *curr = profile_cache->head; |
134 | | |
135 | 195k | if (key_test == 0) |
136 | 228 | return NULL; |
137 | | |
138 | | /* Look through the cache for the key. If found, move to MRU */ |
139 | 278k | while (curr != NULL ){ |
140 | 246k | if (curr->key == key_test){ |
141 | 162k | if_debug2m(gs_debug_flag_icc, pgs->memory, |
142 | 162k | "[icc] Found cs = "PRI_INTPTR", hash = %"PRIu64"\n", |
143 | 162k | (intptr_t)curr->color_space, (uint64_t)curr->key); |
144 | | /* If not already at head of list, move this one there */ |
145 | 162k | if (curr != profile_cache->head) { |
146 | | /* We need to move found one to the top of the list. */ |
147 | 40.1k | prev->next = curr->next; |
148 | 40.1k | curr->next = profile_cache->head; |
149 | 40.1k | profile_cache->head = curr; |
150 | 40.1k | } |
151 | 162k | return(curr->color_space); |
152 | 162k | } |
153 | 83.4k | prev = curr; |
154 | 83.4k | curr = curr->next; |
155 | 83.4k | } |
156 | 32.3k | return(NULL); |
157 | 195k | } |
158 | | |
159 | | /* Remove the LRU entry, which ideally is at the bottom. Note that there |
160 | | is no need to have a ref_count in this structure since the color |
161 | | space objects that are the member variables are reference counted themselves */ |
162 | | static void |
163 | | gsicc_remove_cs_entry(gsicc_profile_cache_t *profile_cache) |
164 | 0 | { |
165 | 0 | gs_memory_t *memory = profile_cache->memory; |
166 | 0 | gsicc_profile_entry_t *prev = NULL, *curr = profile_cache->head; |
167 | |
|
168 | | #ifdef DEBUG |
169 | | if (curr == NULL) { |
170 | | emprintf(memory, " attempt to remove from an empty profile cache.\n"); |
171 | | return; /* gs_abort(); */ |
172 | | } |
173 | | #endif |
174 | 0 | while (curr->next != NULL) { |
175 | 0 | prev = curr; |
176 | 0 | curr = curr->next; |
177 | 0 | } |
178 | 0 | profile_cache->num_entries--; |
179 | 0 | if (prev == NULL) { |
180 | | /* No more entries */ |
181 | 0 | profile_cache->head = NULL; |
182 | | #ifdef DEBUG |
183 | | if (profile_cache->num_entries != 0) { |
184 | | emprintf1(memory, "profile cache list empty, but list has num_entries=%d.\n", |
185 | | profile_cache->num_entries); |
186 | | } |
187 | | #endif |
188 | 0 | } else { |
189 | 0 | prev->next = NULL; /* new tail */ |
190 | 0 | } |
191 | | /* Decremented, but someone could still be referencing this */ |
192 | | /* If found again in the source document, it will be regenerated |
193 | | and added back into the cache. */ |
194 | 0 | if_debug2m(gs_debug_flag_icc, memory, |
195 | 0 | "[icc] Remove cs from cache = "PRI_INTPTR", hash = %"PRIu64"\n", |
196 | 0 | (intptr_t)curr->color_space, (uint64_t)curr->key); |
197 | 0 | rc_decrement(curr->color_space, "gsicc_remove_cs_entry"); |
198 | 0 | gs_free_object(memory, curr, "gsicc_remove_cs_entry"); |
199 | 0 | } |