Coverage Report

Created: 2025-06-24 07:01

/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
}