/src/ghostpdl/base/gsmemory.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 | | /* Generic allocator support */ |
18 | | #include "memory_.h" |
19 | | #include "stdint_.h" |
20 | | #include "gdebug.h" |
21 | | #include "gstypes.h" |
22 | | #include "gsmemory.h" |
23 | | #include "gsmdebug.h" |
24 | | #include "gsrefct.h" /* to check prototype */ |
25 | | #include "gsstruct.h" /* ditto */ |
26 | | |
27 | | /* Define the fill patterns for unallocated memory. */ |
28 | | const byte gs_alloc_fill_alloc = 0xa1; |
29 | | const byte gs_alloc_fill_block = 0xb1; |
30 | | const byte gs_alloc_fill_collected = 0xc1; |
31 | | const byte gs_alloc_fill_deleted = 0xd1; |
32 | | const byte gs_alloc_fill_free = 0xf1; |
33 | | |
34 | | /* A 'structure' type descriptor for free blocks. */ |
35 | | gs_public_st_simple(st_free, byte, "(free)"); |
36 | | |
37 | | /* The 'structure' type descriptor for bytes. */ |
38 | | gs_public_st_simple(st_bytes, byte, "bytes"); |
39 | | |
40 | | /* The structure type descriptor for GC roots. */ |
41 | | public_st_gc_root_t(); |
42 | | |
43 | | /* The descriptors for elements and arrays of const strings. */ |
44 | | private_st_const_string(); |
45 | | public_st_const_string_element(); |
46 | | |
47 | | /* GC procedures for bytestrings */ |
48 | | gs_ptr_type_t |
49 | | enum_bytestring(enum_ptr_t *pep, const gs_bytestring *pbs) |
50 | 0 | { |
51 | 0 | return (pbs->bytes ? ENUM_OBJ(pbs->bytes) : ENUM_STRING(pbs)); |
52 | 0 | } |
53 | | gs_ptr_type_t |
54 | | enum_const_bytestring(enum_ptr_t *pep, const gs_const_bytestring *pbs) |
55 | 0 | { |
56 | 0 | return (pbs->bytes ? ENUM_OBJ(pbs->bytes) : ENUM_CONST_STRING(pbs)); |
57 | 0 | } |
58 | | void |
59 | | reloc_bytestring(gs_bytestring *pbs, gc_state_t *gcst) |
60 | 0 | { |
61 | 0 | if (pbs->bytes) { |
62 | 0 | byte *bytes = pbs->bytes; |
63 | 0 | long offset = pbs->data - bytes; |
64 | |
|
65 | 0 | pbs->bytes = bytes = RELOC_OBJ(bytes); |
66 | 0 | pbs->data = bytes + offset; |
67 | 0 | } else |
68 | 0 | RELOC_STRING_VAR(*(gs_string *)pbs); |
69 | 0 | } |
70 | | void |
71 | | reloc_const_bytestring(gs_const_bytestring *pbs, gc_state_t *gcst) |
72 | 0 | { |
73 | 0 | if (pbs->bytes) { |
74 | 0 | const byte *bytes = pbs->bytes; |
75 | 0 | long offset = pbs->data - bytes; |
76 | |
|
77 | 0 | pbs->bytes = bytes = RELOC_OBJ(bytes); |
78 | 0 | pbs->data = bytes + offset; |
79 | 0 | } else |
80 | 0 | RELOC_CONST_STRING_VAR(*(gs_const_string *)pbs); |
81 | 0 | } |
82 | | |
83 | | /* Fill an unoccupied block with a pattern. */ |
84 | | /* Note that the block size may be too large for a single memset. */ |
85 | | void |
86 | | gs_alloc_memset(void *ptr, int /*byte */ fill, size_t lsize) |
87 | 0 | { |
88 | 0 | size_t msize = lsize; |
89 | 0 | char *p = ptr; |
90 | 0 | int isize; |
91 | |
|
92 | 0 | for (; msize; msize -= isize, p += isize) { |
93 | 0 | isize = min(msize, max_int); |
94 | 0 | memset(p, fill, isize); |
95 | 0 | } |
96 | 0 | } |
97 | | |
98 | | /* |
99 | | * Either allocate (if obj == 0) or resize (if obj != 0) a structure array. |
100 | | * If obj != 0, pstype is used only for checking (in DEBUG configurations). |
101 | | */ |
102 | | void * |
103 | | gs_resize_struct_array(gs_memory_t *mem, void *obj, size_t num_elements, |
104 | | gs_memory_type_ptr_t pstype, client_name_t cname) |
105 | 0 | { |
106 | 0 | if (obj == 0) |
107 | 0 | return gs_alloc_struct_array(mem, num_elements, void, pstype, cname); |
108 | | #ifdef DEBUG |
109 | | if (gs_object_type(mem, obj) != pstype) { |
110 | | lprintf3("resize_struct_array "PRI_INTPTR", type was "PRI_INTPTR", expected "PRI_INTPTR"!\n", |
111 | | (intptr_t)obj, (intptr_t)gs_object_type(mem, obj), (intptr_t)pstype); |
112 | | return 0; |
113 | | } |
114 | | #endif |
115 | 0 | return gs_resize_object(mem, obj, num_elements, cname); |
116 | 0 | } |
117 | | |
118 | | /* Allocate a structure using a "raw memory" allocator. |
119 | | * really just an alias for gs_alloc_struct_immovable |
120 | | * with the clients false expectation that it is saving memory |
121 | | */ |
122 | | |
123 | | void * |
124 | | gs_raw_alloc_struct_immovable(gs_memory_t * rmem, |
125 | | gs_memory_type_ptr_t pstype, |
126 | | client_name_t cname) |
127 | 250M | { |
128 | 250M | return gs_alloc_bytes_immovable(rmem, gs_struct_type_size(pstype), cname); |
129 | 250M | } |
130 | | |
131 | | /* No-op freeing procedures */ |
132 | | void |
133 | | gs_ignore_free_object(gs_memory_t * mem, void *data, client_name_t cname) |
134 | 10.0M | { |
135 | 10.0M | } |
136 | | void |
137 | | gs_ignore_free_string(gs_memory_t * mem, byte * data, size_t nbytes, |
138 | | client_name_t cname) |
139 | 0 | { |
140 | 0 | } |
141 | | |
142 | | /* Deconstifying freeing procedures. */ |
143 | | /* These procedures rely on a severely deprecated pun. */ |
144 | | void |
145 | | gs_free_const_object(gs_memory_t * mem, const void *data, client_name_t cname) |
146 | 1.97M | { |
147 | 1.97M | union { const void *r; void *w; } u; |
148 | | |
149 | 1.97M | u.r = data; |
150 | 1.97M | gs_free_object(mem, u.w, cname); |
151 | 1.97M | } |
152 | | void |
153 | | gs_free_const_string(gs_memory_t * mem, const byte * data, size_t nbytes, |
154 | | client_name_t cname) |
155 | 18.9M | { |
156 | 18.9M | union { const byte *r; byte *w; } u; |
157 | | |
158 | 18.9M | u.r = data; |
159 | 18.9M | gs_free_string(mem, u.w, nbytes, cname); |
160 | 18.9M | } |
161 | | |
162 | | /* Free a [const] bytestring. */ |
163 | | void |
164 | | gs_free_bytestring(gs_memory_t *mem, gs_bytestring *pbs, client_name_t cname) |
165 | 0 | { |
166 | 0 | if (pbs->bytes) |
167 | 0 | gs_free_object(mem, pbs->bytes, cname); |
168 | 0 | else |
169 | 0 | gs_free_string(mem, pbs->data, pbs->size, cname); |
170 | 0 | } |
171 | | void |
172 | | gs_free_const_bytestring(gs_memory_t *mem, gs_const_bytestring *pbs, |
173 | | client_name_t cname) |
174 | 0 | { |
175 | 0 | if (pbs->bytes) |
176 | 0 | gs_free_const_object(mem, pbs->bytes, cname); |
177 | 0 | else |
178 | 0 | gs_free_const_string(mem, pbs->data, pbs->size, cname); |
179 | 0 | } |
180 | | |
181 | | /* No-op consolidation procedure */ |
182 | | void |
183 | | gs_ignore_consolidate_free(gs_memory_t *mem) |
184 | 0 | { |
185 | 0 | } |
186 | | |
187 | | /* No-op pointer enumeration procedure */ |
188 | | ENUM_PTRS_BEGIN_PROC(gs_no_struct_enum_ptrs) |
189 | 27.5M | { |
190 | 27.5M | return 0; |
191 | 27.5M | ENUM_PTRS_END_PROC |
192 | 27.5M | } |
193 | | |
194 | | /* No-op pointer relocation procedure */ |
195 | 57.3M | RELOC_PTRS_BEGIN(gs_no_struct_reloc_ptrs) |
196 | 57.3M | { |
197 | 57.3M | } |
198 | 57.3M | RELOC_PTRS_END |
199 | | |
200 | | /* Get the size of a structure from the descriptor. */ |
201 | | uint |
202 | | gs_struct_type_size(gs_memory_type_ptr_t pstype) |
203 | 260M | { |
204 | 260M | return pstype->ssize; |
205 | 260M | } |
206 | | |
207 | | /* Get the name of a structure from the descriptor. */ |
208 | | struct_name_t |
209 | | gs_struct_type_name(gs_memory_type_ptr_t pstype) |
210 | 6.38k | { |
211 | 6.38k | return pstype->sname; |
212 | 6.38k | } |
213 | | |
214 | | /* Register a structure root. */ |
215 | | int |
216 | | gs_register_struct_root(gs_memory_t *mem, gs_gc_root_t **root, |
217 | | void **pp, client_name_t cname) |
218 | 3.02M | { |
219 | 3.02M | return gs_register_root(mem, root, ptr_struct_type, pp, cname); |
220 | 3.02M | } |
221 | | |
222 | | /* ---------------- Reference counting ---------------- */ |
223 | | |
224 | | #ifdef DEBUG |
225 | | |
226 | | static const char * |
227 | | rc_object_type_name(const void *vp, const rc_header *prc) |
228 | | { |
229 | | gs_memory_type_ptr_t pstype; |
230 | | |
231 | | if (prc->memory == 0) |
232 | | return "(unknown)"; |
233 | | pstype = gs_object_type(prc->memory, vp); |
234 | | if ((uintptr_t)pstype < 10000) |
235 | | return ("?????"); |
236 | | if (prc->free != rc_free_struct_only) { |
237 | | /* |
238 | | * This object might be stack-allocated or have other unusual memory |
239 | | * management properties. Make some reasonableness checks. |
240 | | * ****** THIS IS A HACK. ****** |
241 | | */ |
242 | | long dist; |
243 | | |
244 | | dist = (const char *)&dist - (const char *)vp; |
245 | | if (dist < 10000 && dist > -10000) |
246 | | return "(on stack)"; |
247 | | if ((uintptr_t)pstype < 0x10000) |
248 | | return "(anomalous)"; |
249 | | } |
250 | | return client_name_string(gs_struct_type_name(pstype)); |
251 | | } |
252 | | |
253 | | /* Trace reference count operations. */ |
254 | | void |
255 | | rc_trace_init_free(const void *vp, const rc_header *prc) |
256 | | { |
257 | | dmprintf3(prc->memory, "[^]%s "PRI_INTPTR" init = %ld\n", |
258 | | rc_object_type_name(vp, prc), (intptr_t)vp, (long)prc->ref_count); |
259 | | } |
260 | | void |
261 | | rc_trace_free_struct(const void *vp, const rc_header *prc, client_name_t cname) |
262 | | { |
263 | | dmprintf3(prc->memory, "[^]%s "PRI_INTPTR" => free (%s)\n", |
264 | | rc_object_type_name(vp, prc), |
265 | | (intptr_t)vp, client_name_string(cname)); |
266 | | } |
267 | | void |
268 | | rc_trace_increment(const void *vp, const rc_header *prc) |
269 | | { |
270 | | dmprintf3(prc->memory, "[^]%s "PRI_INTPTR" ++ => %ld\n", |
271 | | rc_object_type_name(vp, prc), |
272 | | (intptr_t)vp, (long)prc->ref_count); |
273 | | } |
274 | | void |
275 | | rc_trace_adjust(const void *vp, const rc_header *prc, int delta, const char *cname) |
276 | | { |
277 | | dmprintf5(prc->memory, "[^]%s "PRI_INTPTR" %+d => %ld (%s)\n", |
278 | | rc_object_type_name(vp, prc), |
279 | | (intptr_t)vp, delta, (long)(prc->ref_count + delta), cname); |
280 | | } |
281 | | |
282 | | #endif /* DEBUG */ |
283 | | |
284 | | /* Normal freeing routine for reference-counted structures. */ |
285 | | void |
286 | | rc_free_struct_only(gs_memory_t * mem, void *data, client_name_t cname) |
287 | 78.6M | { |
288 | 78.6M | if (mem != 0) |
289 | 78.3M | gs_free_object(mem, data, cname); |
290 | 78.6M | } |
291 | | |
292 | | /* ---------------- Basic-structure GC procedures ---------------- */ |
293 | | |
294 | | /* Enumerate pointers */ |
295 | | ENUM_PTRS_BEGIN_PROC(basic_enum_ptrs) |
296 | 1.22G | { |
297 | 1.22G | const gc_struct_data_t *psd = pstype->proc_data; |
298 | | |
299 | | /* This check is primarily for misuse of the alloc_struct_array */ |
300 | | /* with number of elements 0 and allocation not passing 'element' */ |
301 | 1.22G | if (size == 0) { |
302 | | #ifdef DEBUG |
303 | | dmprintf2(mem, " basic_enum_ptrs: Attempt to enum 0 size structure at "PRI_INTPTR", type: %s\n", |
304 | | (intptr_t)vptr, pstype->sname); |
305 | | #endif |
306 | 0 | return 0; |
307 | 0 | } |
308 | 1.22G | if (index < psd->num_ptrs) { |
309 | 811M | const gc_ptr_element_t *ppe = &psd->ptrs[index]; |
310 | 811M | EV_CONST char *pptr = (EV_CONST char *)vptr + ppe->offset; |
311 | | |
312 | | #ifdef DEBUG |
313 | | /* some extra checking to make sure we aren't out of bounds */ |
314 | | if (ppe->offset > size - sizeof(void *)) { |
315 | | dmprintf4(mem, " basic_enum_ptrs: Attempt to enum ptr with offset=%d beyond size=%d: structure at "PRI_INTPTR", type: %s\n", |
316 | | ppe->offset, size, (intptr_t)vptr, pstype->sname); |
317 | | return 0; |
318 | | } |
319 | | #endif |
320 | 811M | switch ((gc_ptr_type_index_t)ppe->type) { |
321 | 808M | case GC_ELT_OBJ: |
322 | 808M | return ENUM_OBJ(*(const void *EV_CONST *)pptr); |
323 | 114k | case GC_ELT_STRING: |
324 | 114k | return ENUM_STRING((const gs_string *)pptr); |
325 | 3.11M | case GC_ELT_CONST_STRING: |
326 | 3.11M | return ENUM_CONST_STRING((const gs_const_string *)pptr); |
327 | 811M | } |
328 | 811M | } |
329 | 414M | if (!psd->super_type) |
330 | 149M | return 0; |
331 | 264M | return ENUM_USING(*(psd->super_type), |
332 | 414M | (EV_CONST void *) |
333 | 414M | ((EV_CONST char *)vptr + psd->super_offset), |
334 | 414M | pstype->ssize, index - psd->num_ptrs); |
335 | 414M | } |
336 | | ENUM_PTRS_END_PROC |
337 | | |
338 | | /* Relocate pointers */ |
339 | 333M | RELOC_PTRS_BEGIN(basic_reloc_ptrs) |
340 | 333M | { |
341 | 333M | const gc_struct_data_t *psd = pstype->proc_data; |
342 | 333M | uint i; |
343 | | |
344 | 1.14G | for (i = 0; i < psd->num_ptrs; ++i) { |
345 | 811M | const gc_ptr_element_t *ppe = &psd->ptrs[i]; |
346 | 811M | char *pptr = (char *)vptr + ppe->offset; |
347 | | |
348 | 811M | switch ((gc_ptr_type_index_t) ppe->type) { |
349 | 808M | case GC_ELT_OBJ: |
350 | 808M | RELOC_OBJ_VAR(*(void **)pptr); |
351 | 808M | break; |
352 | 114k | case GC_ELT_STRING: |
353 | 114k | RELOC_STRING_VAR(*(gs_string *)pptr); |
354 | 114k | break; |
355 | 3.11M | case GC_ELT_CONST_STRING: |
356 | 3.11M | RELOC_CONST_STRING_VAR(*(gs_const_string *)pptr); |
357 | 3.11M | break; |
358 | 811M | } |
359 | 811M | } |
360 | 333M | if (psd->super_type) |
361 | 79.1M | RELOC_USING(*(psd->super_type), |
362 | 333M | (void *)((char *)vptr + psd->super_offset), |
363 | 333M | pstype->ssize); |
364 | 333M | } RELOC_PTRS_END |