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) 1994, 1995, 1996, 1997, 1998, 1999 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: igcref.c,v 1.6 2004/08/04 19:36:12 stefan Exp $ */
18
/* ref garbage collector for Ghostscript */
19
#include "memory_.h"
20
#include "ghost.h"
21
#include "gsexit.h"
22
#include "gsstruct.h"		/* for gxalloc.h included by iastate.h */
23
#include "iname.h"
24
#include "iastate.h"
25
#include "idebug.h"
26
#include "igc.h"
27
#include "ipacked.h"
28
#include "store.h"		/* for ref_assign_inline */
29
 
30
/* Define whether to trace every step of relocating ref pointers. */
31
#if 0
32
#  define rputc(c) dputc(c)
33
#else
34
#  define rputc(c) DO_NOTHING
35
#endif
36
 
37
/* Forward references */
38
ptr_proc_reloc(igc_reloc_ref_ptr, ref_packed);
39
refs_proc_reloc(igc_reloc_refs);
40
 
41
/*
42
 * Define the 'structure' type descriptor for refs.
43
 * This is special because it has different shared procs.
44
 */
45
private gc_proc_clear_reloc(refs_clear_reloc);
46
private gc_proc_set_reloc(refs_set_reloc);
47
private gc_proc_compact(refs_compact);
48
private const struct_shared_procs_t refs_shared_procs =
49
{refs_clear_reloc, refs_set_reloc, refs_compact};
50
private struct_proc_clear_marks(refs_clear_marks);
51
private struct_proc_reloc_ptrs(refs_do_reloc);
52
const gs_memory_struct_type_t st_refs =
53
{sizeof(ref), "refs", &refs_shared_procs, refs_clear_marks, 0, refs_do_reloc};
54
 
55
/*
56
 * Define the GC procedures for structs that actually contain refs.
57
 * These are special because the shared refs_* procedures
58
 * are never called.  Instead, we unmark the individual refs in clear_marks,
59
 * disregard refs_*_reloc (because we will never relocate a ptr_ref_type
60
 * pointer pointing into the structure), disregard refs_compact (because
61
 * compaction is never required), and remove the marks in reloc_ptrs.
62
 * See also the comment about ptr_ref_type in imemory.h.
63
 */
64
CLEAR_MARKS_PROC(ref_struct_clear_marks)
65
{
66
    ref *pref = (ref *) vptr;
67
    ref *end = (ref *) ((char *)vptr + size);
68
 
69
    for (; pref < end; pref++)
70
	r_clear_attrs(pref, l_mark);
71
}
72
ENUM_PTRS_BEGIN_PROC(ref_struct_enum_ptrs)
73
{
74
    if (index >= size / sizeof(ref))
75
	return 0;
76
    pep->ptr = (const ref *)vptr + index;
77
    return ptr_ref_type;
78
    ENUM_PTRS_END_PROC
79
}
80
RELOC_PTRS_BEGIN(ref_struct_reloc_ptrs)
81
{
82
    vm_spaces spaces = gcst->spaces;
83
    const gs_memory_t *cmem = space_system->stable_memory;
84
 
85
    ref *beg = vptr;
86
    ref *end = (ref *) ((char *)vptr + size);
87
 
88
    igc_reloc_refs((ref_packed *) beg, (ref_packed *) end, gcst);
89
    ref_struct_clear_marks(cmem, vptr, size, pstype);
90
} RELOC_PTRS_END
91
 
92
/* ------ Unmarking phase ------ */
93
 
94
/* Unmark a single ref. */
95
void
96
ptr_ref_unmark(enum_ptr_t *pep, gc_state_t * ignored)
97
{
98
    ref_packed *rpp = (ref_packed *)pep->ptr;
99
 
100
    if (r_is_packed(rpp))
101
	r_clear_pmark(rpp);
102
    else
103
	r_clear_attrs((ref *)rpp, l_mark);
104
}
105
 
106
/* Unmarking routine for ref objects. */
107
private void
108
refs_clear_marks(const gs_memory_t *cmem, 
109
		 void /*obj_header_t */ *vptr, uint size,
110
		 const gs_memory_struct_type_t * pstype)
111
{
112
    ref_packed *rp = (ref_packed *) vptr;
113
    ref_packed *end = (ref_packed *) ((byte *) vptr + size);
114
 
115
    /* Since the last ref is full-size, we only need to check for */
116
    /* the end of the block when we see one of those. */
117
    for (;;) {
118
	if (r_is_packed(rp)) {
119
#ifdef DEBUG
120
	    if (gs_debug_c('8')) {
121
		dlprintf1("  [8]unmark packed 0x%lx ", (ulong) rp);
122
		debug_print_ref(cmem, (const ref *)rp);
123
		dputs("\n");
124
	    }
125
#endif
126
	    r_clear_pmark(rp);
127
	    rp++;
128
	} else {		/* full-size ref */
129
	    ref *const pref = (ref *)rp;
130
 
131
#ifdef DEBUG
132
	    if (gs_debug_c('8')) {
133
		dlprintf1("  [8]unmark ref 0x%lx ", (ulong) rp);
134
		debug_print_ref(cmem, pref);
135
		dputs("\n");
136
	    }
137
#endif
138
	    r_clear_attrs(pref, l_mark);
139
	    rp += packed_per_ref;
140
	    if (rp >= (ref_packed *) end)
141
		break;
142
	}
143
    }
144
}
145
 
146
/* ------ Marking phase ------ */
147
 
148
/* Mark a ref.  Return true if new mark. */
149
bool
150
ptr_ref_mark(enum_ptr_t *pep, gc_state_t * ignored)
151
{
152
    ref_packed *rpp = (void *)pep->ptr;
153
 
154
    if (r_is_packed(rpp)) {
155
	if (r_has_pmark(rpp))
156
	    return false;
157
	r_set_pmark(rpp);
158
    } else {
159
	ref *const pref = (ref *)rpp;
160
 
161
	if (r_has_attr(pref, l_mark))
162
	    return false;
163
	r_set_attrs(pref, l_mark);
164
    }
165
    return true;
166
}
167
 
168
/* ------ Relocation planning phase ------ */
169
 
170
/*
171
 * We store relocation in the size field of refs that don't use it,
172
 * so that we don't have to scan all the way to an unmarked object.
173
 * We must avoid nulls, which sometimes have useful information
174
 * in their size fields, and the types above t_next_index, which are
175
 * actually operators in disguise and also use the size field.
176
 */
177
 
178
/* Clear the relocation for a ref object. */
179
private void
180
refs_clear_reloc(obj_header_t *hdr, uint size)
181
{
182
    ref_packed *rp = (ref_packed *) (hdr + 1);
183
    ref_packed *end = (ref_packed *) ((byte *) rp + size);
184
 
185
    while (rp < end) {
186
	if (r_is_packed(rp))
187
	    rp++;
188
	else {
189
	    /* Full-size ref.  Store the relocation here if possible. */
190
	    ref *const pref = (ref *)rp;
191
 
192
	    if (!ref_type_uses_size_or_null(r_type(pref))) {
193
		if_debug1('8', "  [8]clearing reloc at 0x%lx\n", (ulong) rp);
194
		r_set_size(pref, 0);
195
	    }
196
	    rp += packed_per_ref;
197
	}
198
    }
199
}
200
 
201
/* Set the relocation for a ref object. */
202
private bool
203
refs_set_reloc(obj_header_t * hdr, uint reloc, uint size)
204
{
205
    ref_packed *rp = (ref_packed *) (hdr + 1);
206
    ref_packed *end = (ref_packed *) ((byte *) rp + size);
207
    uint freed = 0;
208
 
209
    /*
210
     * We have to be careful to keep refs aligned properly.
211
     * For the moment, we do this by either keeping or discarding
212
     * an entire (aligned) block of align_packed_per_ref packed elements
213
     * as a unit.  We know that align_packed_per_ref <= packed_per_ref,
214
     * and we also know that packed refs are always allocated in blocks
215
     * of align_packed_per_ref, so this makes things relatively easy.
216
     */
217
    while (rp < end) {
218
	if (r_is_packed(rp)) {
219
#if align_packed_per_ref == 1
220
	    if (r_has_pmark(rp)) {
221
		if_debug1('8',
222
			  "  [8]packed ref 0x%lx is marked\n",
223
			  (ulong) rp);
224
		rp++;
225
	    } else {
226
#else
227
	    int i;
228
 
229
	    /*
230
	     * Note: align_packed_per_ref is typically
231
	     * 2 or 4 for 32-bit processors.
232
	     */
233
#define all_marked (align_packed_per_ref * lp_mark)
234
# if align_packed_per_ref == 2
235
#  if arch_sizeof_int == arch_sizeof_short * 2
236
#    undef all_marked
237
#    define all_marked ( (lp_mark << (sizeof(short) * 8)) + lp_mark )
238
#    define marked (*(int *)rp & all_marked)
239
#  else
240
#    define marked ((*rp & lp_mark) + (rp[1] & lp_mark))
241
#  endif
242
# else
243
#  if align_packed_per_ref == 4
244
#    define marked ((*rp & lp_mark) + (rp[1] & lp_mark) +\
245
		    (rp[2] & lp_mark) + (rp[3] & lp_mark))
246
#  else
247
	    /*
248
	     * The value of marked is logically a uint, not an int:
249
	     * we declare it as int only to avoid a compiler warning
250
	     * message about using a non-int value in a switch statement.
251
	     */
252
	    int marked = *rp & lp_mark;
253
 
254
	    for (i = 1; i < align_packed_per_ref; i++)
255
		marked += rp[i] & lp_mark;
256
#  endif
257
# endif
258
	    /*
259
	     * Now marked is lp_mark * the number of marked
260
	     * packed refs in the aligned block, except for
261
	     * a couple of special cases above.
262
	     */
263
	    switch (marked) {
264
		case all_marked:
265
		    if_debug2('8',
266
			      "  [8]packed refs 0x%lx..0x%lx are marked\n",
267
			      (ulong) rp,
268
			      (ulong) (rp + (align_packed_per_ref - 1)));
269
		    rp += align_packed_per_ref;
270
		    break;
271
		default:
272
		    /* At least one packed ref in the block */
273
		    /* is marked: Keep the whole block. */
274
		    for (i = align_packed_per_ref; i--; rp++) {
275
			r_set_pmark(rp);
276
			if_debug1('8',
277
				  "  [8]packed ref 0x%lx is marked\n",
278
				  (ulong) rp);
279
		    }
280
		    break;
281
		case 0:
282
#endif
283
		    if_debug2('8', "  [8]%d packed ref(s) at 0x%lx are unmarked\n",
284
			      align_packed_per_ref, (ulong) rp);
285
		    {
286
			uint rel = reloc + freed;
287
 
288
			/* Change this to an integer so we can */
289
			/* store the relocation here. */
290
			*rp = pt_tag(pt_integer) +
291
			    min(rel, packed_max_value);
292
		    }
293
		    rp += align_packed_per_ref;
294
		    freed += sizeof(ref_packed) * align_packed_per_ref;
295
	    }
296
	} else {		/* full-size ref */
297
	    uint rel = reloc + freed;
298
 
299
	    /* The following assignment is logically */
300
	    /* unnecessary; we do it only for convenience */
301
	    /* in debugging. */
302
	    ref *pref = (ref *) rp;
303
 
304
	    if (!r_has_attr(pref, l_mark)) {
305
		if_debug1('8', "  [8]ref 0x%lx is unmarked\n",
306
			  (ulong) pref);
307
		/* Change this to a mark so we can */
308
		/* store the relocation. */
309
		r_set_type(pref, t_mark);
310
		r_set_size(pref, rel);
311
		freed += sizeof(ref);
312
	    } else {
313
		if_debug1('8', "  [8]ref 0x%lx is marked\n",
314
			  (ulong) pref);
315
		/* Store the relocation here if possible. */
316
		if (!ref_type_uses_size_or_null(r_type(pref))) {
317
		    if_debug2('8', "  [8]storing reloc %u at 0x%lx\n",
318
			      rel, (ulong) pref);
319
		    r_set_size(pref, rel);
320
		}
321
	    }
322
	    rp += packed_per_ref;
323
	}
324
    }
325
    if_debug3('7', " [7]at end of refs 0x%lx, size = %u, freed = %u\n",
326
	      (ulong) (hdr + 1), size, freed);
327
    if (freed == size)
328
	return false;
329
#if arch_sizeof_int > arch_sizeof_short
330
    /*
331
     * If the final relocation can't fit in the r_size field
332
     * (which can't happen if the object shares a chunk with
333
     * any other objects, so we know reloc = 0 in this case),
334
     * we have to keep the entire object unless there are no
335
     * references to any ref in it.
336
     */
337
    if (freed <= max_ushort)
338
	return true;
339
    /*
340
     * We have to mark all surviving refs, but we also must
341
     * overwrite any non-surviving refs with something that
342
     * doesn't contain any pointers.
343
     */
344
    rp = (ref_packed *) (hdr + 1);
345
    while (rp < end) {
346
	if (r_is_packed(rp)) {
347
	    if (!r_has_pmark(rp))
348
		*rp = pt_tag(pt_integer) | lp_mark;
349
	    ++rp;
350
	} else {		/* The following assignment is logically */
351
	    /* unnecessary; we do it only for convenience */
352
	    /* in debugging. */
353
	    ref *pref = (ref *) rp;
354
 
355
	    if (!r_has_attr(pref, l_mark)) {
356
		r_set_type_attrs(pref, t_mark, l_mark);
357
		r_set_size(pref, reloc);
358
	    } else {
359
		if (!ref_type_uses_size_or_null(r_type(pref)))
360
		    r_set_size(pref, reloc);
361
	    }
362
	    rp += packed_per_ref;
363
	}
364
    }
365
    /* The last ref has to remain unmarked. */
366
    r_clear_attrs((ref *) rp - 1, l_mark);
367
#endif
368
    return true;
369
}
370
 
371
/* ------ Relocation phase ------ */
372
 
373
/* Relocate all the pointers in a block of refs. */
374
private void
375
refs_do_reloc(void /*obj_header_t */ *vptr, uint size,
376
	      const gs_memory_struct_type_t * pstype, gc_state_t * gcst)
377
{
378
    igc_reloc_refs((ref_packed *) vptr,
379
		   (ref_packed *) ((char *)vptr + size),
380
		   gcst);
381
}
382
/* Relocate the contents of a block of refs. */
383
/* If gcst->relocating_untraced is true, we are relocating pointers from an */
384
/* untraced space, so relocate all refs, not just marked ones. */
385
void
386
igc_reloc_refs(ref_packed * from, ref_packed * to, gc_state_t * gcst)
387
{
388
    int min_trace = gcst->min_collect;
389
    ref_packed *rp = from;
390
    bool do_all = gcst->relocating_untraced; 
391
 
392
    vm_spaces spaces = gcst->spaces;
393
    const gs_memory_t *cmem = space_system->stable_memory;
394
 
395
    while (rp < to) {
396
	ref *pref;
397
#ifdef DEBUG
398
	const void *before = 0;
399
	const void *after = 0;
400
# define DO_RELOC(var, stat)\
401
    BEGIN before = (var); stat; after = (var); END
402
# define SET_RELOC(var, expr)\
403
    BEGIN before = (var); after = (var) = (expr); END
404
#else
405
# define DO_RELOC(var, stat) stat
406
# define SET_RELOC(var, expr) var = expr
407
#endif
408
 
409
	if (r_is_packed(rp)) {
410
	    rp++;
411
	    continue;
412
	}
413
	/* The following assignment is logically unnecessary; */
414
	/* we do it only for convenience in debugging. */
415
	pref = (ref *) rp;
416
	if_debug3('8', "  [8]relocating %s %d ref at 0x%lx",
417
		  (r_has_attr(pref, l_mark) ? "marked" : "unmarked"),
418
		  r_btype(pref), (ulong) pref);
419
	if ((r_has_attr(pref, l_mark) || do_all) &&
420
	    r_space(pref) >= min_trace
421
	    ) {
422
	    switch (r_type(pref)) {
423
		    /* Struct cases */
424
		case t_file:
425
		    DO_RELOC(pref->value.pfile, RELOC_VAR(pref->value.pfile));
426
		    break;
427
		case t_device:
428
		    DO_RELOC(pref->value.pdevice,
429
			     RELOC_VAR(pref->value.pdevice));
430
		    break;
431
		case t_fontID:
432
		case t_struct:
433
		case t_astruct:
434
		    DO_RELOC(pref->value.pstruct,
435
			     RELOC_VAR(pref->value.pstruct));
436
		    break;
437
		    /* Non-trivial non-struct cases */
438
		case t_dictionary:
439
		    rputc('d');
440
		    SET_RELOC(pref->value.pdict,
441
			      (dict *)igc_reloc_ref_ptr((ref_packed *)pref->value.pdict, gcst));
442
		    break;
443
		case t_array:
444
		    {
445
			uint size = r_size(pref);
446
 
447
			if (size != 0) {	/* value.refs might be NULL */
448
 
449
			    /*
450
			     * If the array is large, we allocated it in its
451
			     * own object (at least originally -- this might
452
			     * be a pointer to a subarray.)  In this case,
453
			     * we know it is the only object in its
454
			     * containing st_refs object, so we know that
455
			     * the mark containing the relocation appears
456
			     * just after it.
457
			     */
458
			    if (size < max_size_st_refs / sizeof(ref)) {
459
				rputc('a');
460
				SET_RELOC(pref->value.refs,
461
				    (ref *) igc_reloc_ref_ptr(
462
				     (ref_packed *) pref->value.refs, gcst));
463
			    } else {
464
				rputc('A');
465
				/*
466
				 * See the t_shortarray case below for why we
467
				 * decrement size.
468
				 */
469
				--size;
470
				SET_RELOC(pref->value.refs,
471
				    (ref *) igc_reloc_ref_ptr(
472
				   (ref_packed *) (pref->value.refs + size),
473
							       gcst) - size);
474
			    }
475
			}
476
		    }
477
		    break;
478
		case t_mixedarray:
479
		    if (r_size(pref) != 0) {	/* value.refs might be NULL */
480
			rputc('m');
481
			SET_RELOC(pref->value.packed,
482
				  igc_reloc_ref_ptr(pref->value.packed, gcst));
483
		    }
484
		    break;
485
		case t_shortarray:
486
		    {
487
			uint size = r_size(pref);
488
 
489
			/*
490
			 * Since we know that igc_reloc_ref_ptr works by
491
			 * scanning forward, and we know that all the
492
			 * elements of this array itself are marked, we can
493
			 * save some scanning time by relocating the pointer
494
			 * to the end of the array rather than the
495
			 * beginning.
496
			 */
497
			if (size != 0) {	/* value.refs might be NULL */
498
			    rputc('s');
499
			    /*
500
			     * igc_reloc_ref_ptr has to be able to determine
501
			     * whether the pointer points into a space that
502
			     * isn't being collected.  It does this by
503
			     * checking whether the referent of the pointer
504
			     * is marked.  For this reason, we have to pass
505
			     * a pointer to the last real element of the
506
			     * array, rather than just beyond it.
507
			     */
508
			    --size;
509
			    SET_RELOC(pref->value.packed,
510
				igc_reloc_ref_ptr(pref->value.packed + size,
511
						  gcst) - size);
512
			}
513
		    }
514
		    break;
515
		case t_name:
516
		    {
517
			void *psub = name_ref_sub_table(cmem, pref);
518
			void *rsub = RELOC_OBJ(psub); /* gcst implicit */
519
 
520
			SET_RELOC(pref->value.pname,
521
				  (name *)
522
				  ((char *)rsub + ((char *)pref->value.pname -
523
						   (char *)psub)));
524
		    } break;
525
		case t_string:
526
		    {
527
			gs_string str;
528
 
529
			str.data = pref->value.bytes;
530
			str.size = r_size(pref);
531
 
532
			DO_RELOC(str.data, RELOC_STRING_VAR(str));
533
			pref->value.bytes = str.data;
534
		    }
535
		    break;
536
		case t_oparray:
537
		    rputc('o');
538
		    SET_RELOC(pref->value.const_refs,
539
			(const ref *)igc_reloc_ref_ptr((const ref_packed *)pref->value.const_refs, gcst));
540
		    break;
541
		default:
542
		    goto no_reloc; /* don't print trace message */
543
	    }
544
	    if_debug2('8', ", 0x%lx => 0x%lx", (ulong)before, (ulong)after);
545
	}
546
no_reloc:
547
	if_debug0('8', "\n");
548
	rp += packed_per_ref;
549
    }
550
}
551
 
552
/* Relocate a pointer to a ref. */
553
/* See gsmemory.h for why the argument is const and the result is not. */
554
ref_packed *
555
igc_reloc_ref_ptr(const ref_packed * prp, gc_state_t *gcst)
556
{
557
    /*
558
     * Search forward for relocation.  This algorithm is intrinsically very
559
     * inefficient; we hope eventually to replace it with a better one.
560
     */
561
    const ref_packed *rp = prp;
562
    uint dec = 0;
563
#ifdef ALIGNMENT_ALIASING_BUG
564
    const ref *rpref;
565
# define RP_REF(rp) (rpref = (const ref *)rp, rpref)
566
#else
567
# define RP_REF(rp) ((const ref *)rp)
568
#endif
569
    /*
570
     * Iff this pointer points into a space that wasn't traced,
571
     * the referent won't be marked.  In this case, we shouldn't
572
     * do any relocation.  Check for this first.
573
     */
574
    if (r_is_packed(rp)) {
575
	if (!r_has_pmark(rp))
576
	    goto ret_rp;
577
    } else {
578
	if (!r_has_attr(RP_REF(rp), l_mark))
579
	    goto ret_rp;
580
    }
581
    for (;;) {
582
 
583
	if (r_is_packed(rp)) {
584
	    /*
585
	     * Normally, an unmarked packed ref will be an
586
	     * integer whose value is the amount of relocation.
587
	     * However, the relocation value might have been
588
	     * too large to fit.  If this is the case, for
589
	     * each such unmarked packed ref we pass over,
590
	     * we have to decrement the final relocation.
591
	     */
592
	    rputc((*rp & lp_mark ? '1' : '0'));
593
	    if (!(*rp & lp_mark)) {
594
		if (*rp != pt_tag(pt_integer) + packed_max_value) {
595
		    /* This is a stored relocation value. */
596
		    rputc('\n');
597
		    rp = print_reloc(prp, "ref",
598
				     (const ref_packed *)
599
				     ((const char *)prp -
600
				      (*rp & packed_value_mask) + dec));
601
		    break;
602
		}
603
		/*
604
		 * We know this is the first of an aligned block
605
		 * of packed refs.  Skip over the entire block,
606
		 * decrementing the final relocation.
607
		 */
608
		dec += sizeof(ref_packed) * align_packed_per_ref;
609
		rp += align_packed_per_ref;
610
	    } else
611
		rp++;
612
	    continue;
613
	}
614
	if (!ref_type_uses_size_or_null(r_type(RP_REF(rp)))) {
615
	    /* reloc is in r_size */
616
	    rputc('\n');
617
	    rp = print_reloc(prp, "ref",
618
			     (const ref_packed *)
619
			     (r_size(RP_REF(rp)) == 0 ? prp :
620
			      (const ref_packed *)((const char *)prp -
621
						   r_size(RP_REF(rp)) + dec)));
622
	    break;
623
	}
624
	rputc('u');
625
	rp += packed_per_ref;
626
    }
627
ret_rp:
628
    /* Use a severely deprecated pun to remove the const property. */
629
    {
630
	union { const ref_packed *r; ref_packed *w; } u;
631
 
632
	u.r = rp;
633
	return u.w;
634
    }
635
}
636
 
637
/* ------ Compaction phase ------ */
638
 
639
/* Compact a ref object. */
640
/* Remove the marks at the same time. */
641
private void
642
refs_compact(const gs_memory_t *mem, obj_header_t * pre, obj_header_t * dpre, uint size)
643
{
644
    ref_packed *dest;
645
    ref_packed *src;
646
    ref_packed *end;
647
    uint new_size;
648
 
649
    src = (ref_packed *) (pre + 1);
650
    end = (ref_packed *) ((byte *) src + size);
651
    /*
652
     * We know that a block of refs always ends with an unmarked
653
     * full-size ref, so we only need to check for reaching the end
654
     * of the block when we see one of those.
655
     */
656
    if (dpre == pre)		/* Loop while we don't need to copy. */
657
	for (;;) {
658
	    if (r_is_packed(src)) {
659
		if (!r_has_pmark(src))
660
		    break;
661
		if_debug1('8', "  [8]packed ref 0x%lx \"copied\"\n",
662
			  (ulong) src);
663
		*src &= ~lp_mark;
664
		src++;
665
	    } else {		/* full-size ref */
666
		ref *const pref = (ref *)src;
667
 
668
		if (!r_has_attr(pref, l_mark))
669
		    break;
670
		if_debug1('8', "  [8]ref 0x%lx \"copied\"\n", (ulong) src);
671
		r_clear_attrs(pref, l_mark);
672
		src += packed_per_ref;
673
	    }
674
    } else
675
	*dpre = *pre;
676
    dest = (ref_packed *) ((char *)dpre + ((char *)src - (char *)pre));
677
    for (;;) {
678
	if (r_is_packed(src)) {
679
	    if (r_has_pmark(src)) {
680
		if_debug2('8', "  [8]packed ref 0x%lx copied to 0x%lx\n",
681
			  (ulong) src, (ulong) dest);
682
		*dest++ = *src & ~lp_mark;
683
	    }
684
	    src++;
685
	} else {		/* full-size ref */
686
	    if (r_has_attr((ref *) src, l_mark)) {
687
		ref rtemp;
688
 
689
		if_debug2('8', "  [8]ref 0x%lx copied to 0x%lx\n",
690
			  (ulong) src, (ulong) dest);
691
		/* We can't just use ref_assign_inline, */
692
		/* because the source and destination */
693
		/* might overlap! */
694
		ref_assign_inline(&rtemp, (ref *) src);
695
		r_clear_attrs(&rtemp, l_mark);
696
		ref_assign_inline((ref *) dest, &rtemp);
697
		dest += packed_per_ref;
698
		src += packed_per_ref;
699
	    } else {		/* check for end of block */
700
		src += packed_per_ref;
701
		if (src >= end)
702
		    break;
703
	    }
704
	}
705
    }
706
    new_size = (byte *) dest - (byte *) (dpre + 1) + sizeof(ref);
707
#ifdef DEBUG
708
    /* Check that the relocation came out OK. */
709
    /* NOTE: this check only works within a single chunk. */
710
    if ((byte *) src - (byte *) dest != r_size((ref *) src - 1) + sizeof(ref)) {
711
	lprintf3("Reloc error for refs 0x%lx: reloc = %lu, stored = %u\n",
712
		 (ulong) dpre, (ulong) ((byte *) src - (byte *) dest),
713
		 (uint) r_size((ref *) src - 1));
714
	gs_abort(mem);
715
    }
716
#endif
717
    /* Pad to a multiple of sizeof(ref). */
718
    while (new_size & (sizeof(ref) - 1))
719
	*dest++ = pt_tag(pt_integer),
720
	    new_size += sizeof(ref_packed);
721
    /* We want to make the newly freed space into a free block, */
722
    /* but we can only do this if we have enough room. */
723
    if (size - new_size < sizeof(obj_header_t)) {	/* Not enough room.  Pad to original size. */
724
	while (new_size < size)
725
	    *dest++ = pt_tag(pt_integer),
726
		new_size += sizeof(ref_packed);
727
    } else {
728
	obj_header_t *pfree = (obj_header_t *) ((ref *) dest + 1);
729
 
730
	pfree->o_alone = 0;
731
	pfree->o_size = size - new_size - sizeof(obj_header_t);
732
	pfree->o_type = &st_bytes;
733
    }
734
    /* Re-create the final ref. */
735
    r_set_type((ref *) dest, t_integer);
736
    dpre->o_size = new_size;
737
}