Coverage Report

Created: 2025-06-10 07:27

/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
20.5k
#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.18M
{
54
3.18M
    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.18M
    memory = memory->stable_memory;
59
3.18M
    result = gs_alloc_struct(memory, gsicc_profile_cache_t,
60
3.18M
                             &st_profile_cache, "gsicc_profilecache_new");
61
3.18M
    if ( result == NULL )
62
0
        return(NULL);
63
3.18M
    rc_init_free(result, memory, 1, rc_gsicc_profile_cache_free);
64
3.18M
    result->head = NULL;
65
3.18M
    result->num_entries = 0;
66
3.18M
    result->memory = memory;
67
3.18M
    return(result);
68
3.18M
}
69
70
static void
71
rc_gsicc_profile_cache_free(gs_memory_t * mem, void *ptr_in, client_name_t cname)
72
3.18M
{
73
3.18M
    gsicc_profile_cache_t *profile_cache = (gsicc_profile_cache_t * ) ptr_in;
74
3.18M
    gsicc_profile_entry_t *curr = profile_cache->head, *next;
75
76
3.18M
    assert(mem->stable_memory == profile_cache->memory);
77
3.20M
    while (curr != NULL ){
78
20.5k
        next = curr->next;
79
20.5k
        rc_decrement(curr->color_space, "rc_gsicc_profile_cache_free");
80
20.5k
        gs_free_object(profile_cache->memory, curr,
81
20.5k
                       "rc_gsicc_profile_cache_free");
82
20.5k
        profile_cache->num_entries--;
83
20.5k
        curr = next;
84
20.5k
    }
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.18M
    gs_free_object(profile_cache->memory, profile_cache,
91
3.18M
                   "rc_gsicc_profile_cache_free");
92
3.18M
}
93
94
int
95
gsicc_add_cs(gs_gstate * pgs, gs_color_space * colorspace, uint64_t dictkey)
96
20.8k
{
97
20.8k
    gsicc_profile_entry_t *result;
98
20.8k
    gsicc_profile_cache_t *profile_cache = pgs->icc_profile_cache;
99
20.8k
    gs_memory_t *memory =  profile_cache->memory;
100
101
20.8k
    if (dictkey == 0)
102
232
        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
20.5k
    result = gs_alloc_struct(memory, gsicc_profile_entry_t,
107
20.5k
                             &st_profile_entry, "gsicc_add_cs");
108
20.5k
    if (result == NULL)
109
0
        return_error(gs_error_VMerror);
110
111
    /* If needed, remove an entry (the last one) */
112
20.5k
    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
20.5k
    result->next = profile_cache->head;
118
20.5k
    profile_cache->head = result; /* MRU */
119
20.5k
    result->color_space = colorspace;
120
20.5k
    rc_increment(colorspace);
121
20.5k
    result->key = dictkey;
122
20.5k
    if_debug2m(gs_debug_flag_icc, memory,
123
20.5k
               "[icc] Add cs to cache = "PRI_INTPTR", hash = %"PRIu64"\n",
124
20.5k
               (intptr_t)result->color_space, (uint64_t)result->key);
125
20.5k
    profile_cache->num_entries++;
126
20.5k
    return 0;
127
20.5k
}
128
129
gs_color_space*
130
gsicc_find_cs(uint64_t key_test, gs_gstate * pgs)
131
196k
{
132
196k
    gsicc_profile_cache_t *profile_cache = pgs->icc_profile_cache;
133
196k
    gsicc_profile_entry_t *prev = NULL, *curr = profile_cache->head;
134
135
196k
    if (key_test == 0)
136
232
        return NULL;
137
138
    /* Look through the cache for the key. If found, move to MRU */
139
279k
    while (curr != NULL ){
140
248k
        if (curr->key == key_test){
141
165k
            if_debug2m(gs_debug_flag_icc, pgs->memory,
142
165k
                       "[icc] Found cs = "PRI_INTPTR", hash = %"PRIu64"\n",
143
165k
                       (intptr_t)curr->color_space, (uint64_t)curr->key);
144
            /* If not already at head of list, move this one there */
145
165k
            if (curr != profile_cache->head) {
146
                /* We need to move found one to the top of the list. */
147
40.9k
                prev->next = curr->next;
148
40.9k
                curr->next = profile_cache->head;
149
40.9k
                profile_cache->head = curr;
150
40.9k
            }
151
165k
            return(curr->color_space);
152
165k
        }
153
82.8k
        prev = curr;
154
82.8k
        curr = curr->next;
155
82.8k
    }
156
31.1k
    return(NULL);
157
196k
}
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
}