Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
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
}