2 |
- |
1 |
/* Copyright (C) 1996, 2000 Aladdin Enterprises. All rights reserved.
|
|
|
2 |
|
|
|
3 |
This software is provided AS-IS with no warranty, either express or
|
|
|
4 |
implied.
|
|
|
5 |
|
|
|
6 |
This software is distributed under license and may not be copied,
|
|
|
7 |
modified or distributed except as expressly authorized under the terms
|
|
|
8 |
of the license contained in the file LICENSE in this distribution.
|
|
|
9 |
|
|
|
10 |
For more information about licensing, please refer to
|
|
|
11 |
http://www.ghostscript.com/licensing/. For information on
|
|
|
12 |
commercial licensing, go to http://www.artifex.com/licensing/ or
|
|
|
13 |
contact Artifex Software, Inc., 101 Lucas Valley Road #110,
|
|
|
14 |
San Rafael, CA 94903, U.S.A., +1(415)492-9861.
|
|
|
15 |
*/
|
|
|
16 |
|
|
|
17 |
/* $Id: gsnogc.c,v 1.10 2002/06/16 05:48:55 lpd Exp $ */
|
|
|
18 |
/* String freelist implementation and ersatz garbage collector */
|
|
|
19 |
#include "gx.h"
|
|
|
20 |
#include "gsmdebug.h"
|
|
|
21 |
#include "gsnogc.h"
|
|
|
22 |
#include "gsstruct.h"
|
|
|
23 |
#include "gxalloc.h"
|
|
|
24 |
|
|
|
25 |
/*
|
|
|
26 |
* We implement string freelists here, because they are only useful
|
|
|
27 |
* in non-garbage-collected environments.
|
|
|
28 |
*/
|
|
|
29 |
|
|
|
30 |
/*
|
|
|
31 |
* Get and put unaligned 32-bit values. NOTE: these procedures must match
|
|
|
32 |
* the value of SFREE_NB defined in gxalloc.h.
|
|
|
33 |
*/
|
|
|
34 |
#define NB SFREE_NB
|
|
|
35 |
#if NB == 4
|
|
|
36 |
private uint
|
|
|
37 |
get_uu32(const byte *ptr)
|
|
|
38 |
{
|
|
|
39 |
return (ptr[0] << 24) + (ptr[1] << 16) + (ptr[2] << 8) + ptr[3];
|
|
|
40 |
}
|
|
|
41 |
private void
|
|
|
42 |
put_uu32(byte *ptr, uint val)
|
|
|
43 |
{
|
|
|
44 |
ptr[0] = val >> 24;
|
|
|
45 |
ptr[1] = (byte)(val >> 16);
|
|
|
46 |
ptr[2] = (byte)(val >> 8);
|
|
|
47 |
ptr[3] = (byte)val;
|
|
|
48 |
}
|
|
|
49 |
#endif /* otherwise, undefined procedures will give an error */
|
|
|
50 |
|
|
|
51 |
/* Allocate a string. */
|
|
|
52 |
/* Scan the current chunk's free list if the request is large enough. */
|
|
|
53 |
/* Currently we require an exact match of the block size. */
|
|
|
54 |
private byte *
|
|
|
55 |
sf_alloc_string(gs_memory_t * mem, uint nbytes, client_name_t cname)
|
|
|
56 |
{
|
|
|
57 |
gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
|
|
|
58 |
|
|
|
59 |
if (nbytes >= 40 && nbytes < imem->large_size) {
|
|
|
60 |
byte *base = csbase(&imem->cc);
|
|
|
61 |
byte *prev = 0;
|
|
|
62 |
uint offset = imem->cc.sfree;
|
|
|
63 |
uint next;
|
|
|
64 |
byte *ptr;
|
|
|
65 |
|
|
|
66 |
for (; offset != 0; prev = ptr, offset = next) {
|
|
|
67 |
ptr = base + offset;
|
|
|
68 |
next = get_uu32(ptr + NB);
|
|
|
69 |
if (get_uu32(ptr) != nbytes)
|
|
|
70 |
continue;
|
|
|
71 |
/* Take this block. */
|
|
|
72 |
if (prev == 0)
|
|
|
73 |
imem->cc.sfree = next;
|
|
|
74 |
else
|
|
|
75 |
put_uu32(prev + NB, next);
|
|
|
76 |
if_debug4('A', "[a%d:+>F]%s(%u) = 0x%lx\n", imem->space,
|
|
|
77 |
client_name_string(cname), nbytes, (ulong) ptr);
|
|
|
78 |
gs_alloc_fill(ptr, gs_alloc_fill_alloc, nbytes);
|
|
|
79 |
imem->lost.strings -= nbytes;
|
|
|
80 |
return ptr;
|
|
|
81 |
}
|
|
|
82 |
}
|
|
|
83 |
return (*gs_ref_memory_procs.alloc_string) (mem, nbytes, cname);
|
|
|
84 |
}
|
|
|
85 |
|
|
|
86 |
/* Free a string. */
|
|
|
87 |
private void
|
|
|
88 |
sf_free_string(gs_memory_t * mem, byte * str, uint size, client_name_t cname)
|
|
|
89 |
{
|
|
|
90 |
gs_ref_memory_t * const imem = (gs_ref_memory_t *)mem;
|
|
|
91 |
chunk_t *cp;
|
|
|
92 |
uint str_offset;
|
|
|
93 |
|
|
|
94 |
if (str == imem->cc.ctop) {
|
|
|
95 |
if_debug4('A', "[a%d:-> ]%s(%u) 0x%lx\n", imem->space,
|
|
|
96 |
client_name_string(cname), size, (ulong) str);
|
|
|
97 |
imem->cc.ctop += size;
|
|
|
98 |
gs_alloc_fill(str, gs_alloc_fill_free, size);
|
|
|
99 |
return;
|
|
|
100 |
}
|
|
|
101 |
if_debug4('A', "[a%d:->#]%s(%u) 0x%lx\n", imem->space,
|
|
|
102 |
client_name_string(cname), size, (ulong) str);
|
|
|
103 |
imem->lost.strings += size;
|
|
|
104 |
if (ptr_is_in_chunk(str, &imem->cc)) {
|
|
|
105 |
cp = &imem->cc;
|
|
|
106 |
/* We already tested for the string being at ctop. */
|
|
|
107 |
} else {
|
|
|
108 |
chunk_locator_t loc;
|
|
|
109 |
|
|
|
110 |
loc.memory = imem;
|
|
|
111 |
loc.cp = imem->clast;
|
|
|
112 |
if (!chunk_locate_ptr(str, &loc))
|
|
|
113 |
return; /* something is probably wrong.... */
|
|
|
114 |
cp = loc.cp;
|
|
|
115 |
if (str == cp->ctop) {
|
|
|
116 |
cp->ctop += size;
|
|
|
117 |
return;
|
|
|
118 |
}
|
|
|
119 |
}
|
|
|
120 |
str_offset = str - csbase(cp);
|
|
|
121 |
if (size >= 2 * NB) {
|
|
|
122 |
byte *prev;
|
|
|
123 |
uint next;
|
|
|
124 |
|
|
|
125 |
put_uu32(str, size);
|
|
|
126 |
if (cp->sfree == 0 || str_offset < cp->sfree) {
|
|
|
127 |
/* Put the string at the head of the free list. */
|
|
|
128 |
put_uu32(str + NB, cp->sfree);
|
|
|
129 |
cp->sfree = str_offset;
|
|
|
130 |
return;
|
|
|
131 |
}
|
|
|
132 |
/* Insert the new string in address order. */
|
|
|
133 |
prev = csbase(cp) + cp->sfree;
|
|
|
134 |
#ifdef DEBUG
|
|
|
135 |
if (gs_debug_c('?')) {
|
|
|
136 |
if (prev < str + size && prev + get_uu32(prev) > str) {
|
|
|
137 |
lprintf4("freeing string 0x%lx(%u), overlaps 0x%lx(%u)!\n",
|
|
|
138 |
(ulong) str, size, (ulong) prev, get_uu32(prev));
|
|
|
139 |
return;
|
|
|
140 |
}
|
|
|
141 |
}
|
|
|
142 |
#endif
|
|
|
143 |
for (;;) {
|
|
|
144 |
next = get_uu32(prev + NB);
|
|
|
145 |
#ifdef DEBUG
|
|
|
146 |
if (gs_debug_c('?') && next != 0) {
|
|
|
147 |
byte *pnext = csbase(cp) + next;
|
|
|
148 |
|
|
|
149 |
if (pnext < str + size && pnext + get_uu32(pnext) > str) {
|
|
|
150 |
lprintf4("freeing string 0x%lx(%u), overlaps 0x%lx(%u)!\n",
|
|
|
151 |
(ulong) str, size, (ulong) pnext, get_uu32(pnext));
|
|
|
152 |
return;
|
|
|
153 |
}
|
|
|
154 |
}
|
|
|
155 |
#endif
|
|
|
156 |
if (next >= str_offset || next == 0)
|
|
|
157 |
break;
|
|
|
158 |
prev = csbase(cp) + next;
|
|
|
159 |
}
|
|
|
160 |
put_uu32(str + NB, next);
|
|
|
161 |
put_uu32(prev + NB, str_offset);
|
|
|
162 |
gs_alloc_fill(str + 2 * NB, gs_alloc_fill_free, size - 2 * NB);
|
|
|
163 |
} else {
|
|
|
164 |
/*
|
|
|
165 |
* Insert the string in the 1-byte free list(s). Note that
|
|
|
166 |
* if it straddles a 256-byte block, we need to do this twice.
|
|
|
167 |
*/
|
|
|
168 |
uint *pfree1 = &cp->sfree1[str_offset >> 8];
|
|
|
169 |
uint count = size;
|
|
|
170 |
byte *prev;
|
|
|
171 |
byte *ptr;
|
|
|
172 |
|
|
|
173 |
if (*pfree1 == 0) {
|
|
|
174 |
*str = 0;
|
|
|
175 |
*pfree1 = str_offset;
|
|
|
176 |
if (!--count)
|
|
|
177 |
return;
|
|
|
178 |
prev = str;
|
|
|
179 |
ptr = str + 1;
|
|
|
180 |
} else if (str_offset < *pfree1) {
|
|
|
181 |
*str = *pfree1 - str_offset;
|
|
|
182 |
*pfree1 = str_offset;
|
|
|
183 |
if (!--count)
|
|
|
184 |
return;
|
|
|
185 |
prev = str;
|
|
|
186 |
ptr = str + 1;
|
|
|
187 |
} else {
|
|
|
188 |
uint next;
|
|
|
189 |
|
|
|
190 |
prev = csbase(cp) + *pfree1;
|
|
|
191 |
while ((next = *prev) != 0 && prev + next < str)
|
|
|
192 |
prev += next;
|
|
|
193 |
ptr = str;
|
|
|
194 |
}
|
|
|
195 |
for (;;) {
|
|
|
196 |
/*
|
|
|
197 |
* Invariants:
|
|
|
198 |
* ptr == str + size - count
|
|
|
199 |
* prev < sfbase + str_offset
|
|
|
200 |
* *prev == 0 || prev + *prev > sfbase + str_offset
|
|
|
201 |
*/
|
|
|
202 |
*ptr = (*prev == 0 ? 0 : prev + *prev - ptr);
|
|
|
203 |
*prev = ptr - prev;
|
|
|
204 |
if (!--count)
|
|
|
205 |
break;
|
|
|
206 |
prev = ptr++;
|
|
|
207 |
if (!(++str_offset & 255)) {
|
|
|
208 |
/* Move to the next block of 256 bytes. */
|
|
|
209 |
++pfree1;
|
|
|
210 |
*ptr = (byte) * pfree1;
|
|
|
211 |
*pfree1 = str_offset;
|
|
|
212 |
if (!--count)
|
|
|
213 |
break;
|
|
|
214 |
prev = ptr++;
|
|
|
215 |
++str_offset;
|
|
|
216 |
}
|
|
|
217 |
}
|
|
|
218 |
}
|
|
|
219 |
}
|
|
|
220 |
|
|
|
221 |
/* Enable or disable freeing. */
|
|
|
222 |
private void
|
|
|
223 |
sf_enable_free(gs_memory_t * mem, bool enable)
|
|
|
224 |
{
|
|
|
225 |
(*gs_ref_memory_procs.enable_free) (mem, enable);
|
|
|
226 |
if (enable)
|
|
|
227 |
mem->procs.free_string = sf_free_string;
|
|
|
228 |
}
|
|
|
229 |
|
|
|
230 |
/* Merge free strings at the bottom of a chunk's string storage. */
|
|
|
231 |
private void
|
|
|
232 |
sf_merge_strings(chunk_t * cp)
|
|
|
233 |
{
|
|
|
234 |
for (;;) {
|
|
|
235 |
byte *ctop = cp->ctop;
|
|
|
236 |
uint top_offset = ctop - csbase(cp);
|
|
|
237 |
uint *pfree1;
|
|
|
238 |
|
|
|
239 |
if (cp->sfree == top_offset) { /* Merge a large free block. */
|
|
|
240 |
cp->sfree = get_uu32(ctop + NB);
|
|
|
241 |
cp->ctop += get_uu32(ctop);
|
|
|
242 |
continue;
|
|
|
243 |
}
|
|
|
244 |
if (!cp->sfree1)
|
|
|
245 |
break;
|
|
|
246 |
pfree1 = &cp->sfree1[top_offset >> 8];
|
|
|
247 |
if (*pfree1 != top_offset)
|
|
|
248 |
break;
|
|
|
249 |
/* Merge a small (1-byte) free block. */
|
|
|
250 |
*pfree1 = (*ctop ? *pfree1 + *ctop : 0);
|
|
|
251 |
cp->ctop++;
|
|
|
252 |
}
|
|
|
253 |
}
|
|
|
254 |
|
|
|
255 |
/* Consolidate free space. */
|
|
|
256 |
private void
|
|
|
257 |
sf_consolidate_free(gs_memory_t *mem)
|
|
|
258 |
{
|
|
|
259 |
gs_ref_memory_t *imem = (gs_ref_memory_t *)mem;
|
|
|
260 |
chunk_t *cp;
|
|
|
261 |
|
|
|
262 |
alloc_close_chunk(imem);
|
|
|
263 |
for (cp = imem->clast; cp != 0; cp = cp->cprev) {
|
|
|
264 |
byte *top = cp->ctop;
|
|
|
265 |
|
|
|
266 |
sf_merge_strings(cp);
|
|
|
267 |
imem->lost.strings -= cp->ctop - top;
|
|
|
268 |
if (cp->ctop == cp->climit && cp->smark_size != 0) {
|
|
|
269 |
/*
|
|
|
270 |
* No string space is being used. Since we are not using the
|
|
|
271 |
* 'string marking table' for GC, we can recover its space by
|
|
|
272 |
* deleting the smark area, setting smark_size = 0, and sliding
|
|
|
273 |
* up ctop and climit. We also need to recompute the size of
|
|
|
274 |
* the string freelist area (it will be larger, since the
|
|
|
275 |
* space potentially allocated to strings is larger).
|
|
|
276 |
*/
|
|
|
277 |
cp->smark_size = 0;
|
|
|
278 |
cp->smark = 0;
|
|
|
279 |
/*
|
|
|
280 |
* Reserve enough space for the string freelist all the way to
|
|
|
281 |
* cend even though the climit will be moved to before the
|
|
|
282 |
* freelist area. This recovers most of the space.
|
|
|
283 |
*/
|
|
|
284 |
cp->climit = cp->cend;
|
|
|
285 |
cp->climit -= STRING_FREELIST_SPACE(cp);
|
|
|
286 |
cp->ctop = cp->climit;
|
|
|
287 |
alloc_init_free_strings(cp);
|
|
|
288 |
}
|
|
|
289 |
}
|
|
|
290 |
alloc_open_chunk(imem);
|
|
|
291 |
|
|
|
292 |
/* Merge free objects, detecting entirely free chunks. */
|
|
|
293 |
ialloc_consolidate_free(imem);
|
|
|
294 |
}
|
|
|
295 |
|
|
|
296 |
/*
|
|
|
297 |
* This procedure has the same API as the garbage collector used by the
|
|
|
298 |
* PostScript interpreter, but it is designed to be used in environments
|
|
|
299 |
* that don't need garbage collection and don't use save/restore. All it
|
|
|
300 |
* does is coalesce free blocks at the high end of the object area of each
|
|
|
301 |
* chunk, and free strings at the low end of the string area, and then if
|
|
|
302 |
* not 'controlled' memory, free completely empty chunks.
|
|
|
303 |
*
|
|
|
304 |
* Note that any string marking area will be added to the free space
|
|
|
305 |
* within the chunk if possible.
|
|
|
306 |
*/
|
|
|
307 |
|
|
|
308 |
private void use_string_freelists(gs_ref_memory_t *mem);
|
|
|
309 |
void
|
|
|
310 |
gs_nogc_reclaim(vm_spaces * pspaces, bool global)
|
|
|
311 |
{
|
|
|
312 |
int space;
|
|
|
313 |
gs_ref_memory_t *mem_prev = 0;
|
|
|
314 |
|
|
|
315 |
for (space = 0; space < countof(pspaces->memories.indexed); ++space) {
|
|
|
316 |
gs_ref_memory_t *mem = pspaces->memories.indexed[space];
|
|
|
317 |
|
|
|
318 |
if (mem == 0 || mem == mem_prev)
|
|
|
319 |
continue;
|
|
|
320 |
mem_prev = mem;
|
|
|
321 |
use_string_freelists(mem);
|
|
|
322 |
if (mem->stable_memory != (gs_memory_t *)mem &&
|
|
|
323 |
mem->stable_memory != 0
|
|
|
324 |
)
|
|
|
325 |
use_string_freelists((gs_ref_memory_t *)mem->stable_memory);
|
|
|
326 |
}
|
|
|
327 |
}
|
|
|
328 |
#ifdef VMS
|
|
|
329 |
#pragma optimize ansi_alias=off
|
|
|
330 |
#endif
|
|
|
331 |
private void
|
|
|
332 |
use_string_freelists(gs_ref_memory_t *rmem)
|
|
|
333 |
{
|
|
|
334 |
/*
|
|
|
335 |
* ANSI made an incompatible change to the C language standard that
|
|
|
336 |
* caused the following to generate aliasing warnings:
|
|
|
337 |
gs_memory_t *mem = (gs_memory_t *)rmem;
|
|
|
338 |
* Consequently, we now use rmem rather than mem in the assignments
|
|
|
339 |
* below, even though this degrades code readability by obscuring the
|
|
|
340 |
* fact that they are only manipulating fields of the more abstract
|
|
|
341 |
* superclass.
|
|
|
342 |
* For OpenVMS this still gets us a warning so we switch the warning
|
|
|
343 |
* message off.
|
|
|
344 |
*/
|
|
|
345 |
#ifdef VMS
|
|
|
346 |
#pragma message disable BADANSIALIAS
|
|
|
347 |
#endif
|
|
|
348 |
|
|
|
349 |
/* Change the allocator to use string freelists in the future. */
|
|
|
350 |
rmem->procs.alloc_string = sf_alloc_string;
|
|
|
351 |
if (rmem->procs.free_string != gs_ignore_free_string)
|
|
|
352 |
rmem->procs.free_string = sf_free_string;
|
|
|
353 |
rmem->procs.enable_free = sf_enable_free;
|
|
|
354 |
rmem->procs.consolidate_free = sf_consolidate_free;
|
|
|
355 |
|
|
|
356 |
/* Merge free objects, detecting entirely free chunks. */
|
|
|
357 |
gs_consolidate_free((gs_memory_t *)rmem);
|
|
|
358 |
}
|