~ubuntu-branches/ubuntu/jaunty/ghostscript/jaunty-updates

« back to all changes in this revision

Viewing changes to base/gsmchunk.c

  • Committer: Bazaar Package Importer
  • Author(s): Till Kamppeter
  • Date: 2009-01-20 16:40:45 UTC
  • mfrom: (1.1.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20090120164045-lnfhi0n30o5lwhwa
Tags: 8.64.dfsg.1~svn9377-0ubuntu1
* New upstream release (SVN rev 9377)
   o Fixes many bugs concerning PDF rendering, to make the PDF printing
     workflow correctly working.
   o Fixes long-standing bugs in many drivers, like input paper tray and
     duplex options not working for the built-in PCL 4, 5, 5c, 5e, and
     6/XL drivers, PDF input not working for bjc600, bjc800, and cups
     output devices, several options not working and uninitialized
     memory with cups output device.
   o Merged nearly all patches of the Ubuntu and Debian packages upstream.
   o Fixes LP: #317810, LP: #314439, LP: #314018.
* debian/patches/03_libpaper_support.dpatch,
  debian/patches/11_gs-cjk_font_glyph_handling_fix.dpatch,
  debian/patches/12_gs-cjk_vertical_writing_metrics_fix.dpatch,
  debian/patches/13_gs-cjk_cjkps_examples.dpatch,
  debian/patches/20_bbox_segv_fix.dpatch,
  debian/patches/21_brother_7x0_gdi_fix.dpatch,
  debian/patches/22_epsn_margin_workaround.dpatch,
  debian/patches/24_gs_man_fix.dpatch,
  debian/patches/25_toolbin_insecure_tmp_usage_fix.dpatch,
  debian/patches/26_assorted_script_fixes.dpatch,
  debian/patches/29_gs_css_fix.dpatch,
  debian/patches/30_ps2pdf_man_improvement.dpatch,
  debian/patches/31_fix-gc-sigbus.dpatch,
  debian/patches/34_ftbfs-on-hurd-fix.dpatch,
  debian/patches/35_disable_libcairo.dpatch,
  debian/patches/38_pxl-duplex.dpatch,
  debian/patches/39_pxl-resolution.dpatch,
  debian/patches/42_gs-init-ps-delaybind-fix.dpatch,
  debian/patches/45_bjc600-bjc800-pdf-input.dpatch,
  debian/patches/48_cups-output-device-pdf-duplex-uninitialized-memory-fix.dpatch,
  debian/patches/50_lips4-floating-point-exception.dpatch,
  debian/patches/52_cups-device-logging.dpatch,
  debian/patches/55_pcl-input-slot-fix.dpatch,
  debian/patches/57_pxl-input-slot-fix.dpatch,
  debian/patches/60_pxl-cups-driver-pdf.dpatch,
  debian/patches/62_onebitcmyk-pdf.dpatch,
  debian/patches/65_too-big-temp-files-1.dpatch,
  debian/patches/67_too-big-temp-files-2.dpatch,
  debian/patches/70_take-into-account-data-in-stream-buffer-before-refill.dpatch:
  Removed, applied upstream.
* debian/patches/01_docdir_fix_for_debian.dpatch,
  debian/patches/02_gs_man_fix_debian.dpatch,
  debian/patches/01_docdir-fix-for-debian.dpatch,
  debian/patches/02_docdir-fix-for-debian.dpatch: Renamed patches to
  make merging with Debian easier.
* debian/patches/32_improve-handling-of-media-size-changes-from-gv.dpatch, 
  debian/patches/33_bad-params-to-xinitimage-on-large-bitmaps.dpatch:
  regenerated for new source directory structure.
* debian/rules: Corrected paths to remove cidfmap (it is in Resource/Init/
  in GS 8.64) and to install headers (source paths are psi/ and base/ now).
* debian/rules: Remove all fontmaps, as DeFoMa replaces them.
* debian/local/pdftoraster/pdftoraster.c,
  debian/local/pdftoraster/pdftoraster.convs, debian/rules: Removed
  added pdftoraster filter and use the one which comes with Ghostscript.
* debian/ghostscript.links: s/8.63/8.64/

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2001-2006 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, modified
 
8
   or distributed except as expressly authorized under the terms of that
 
9
   license.  Refer to licensing information at http://www.artifex.com/
 
10
   or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
 
11
   San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
 
12
*/
 
13
 
 
14
/* $Id: gsmchunk.c 8928 2008-08-03 12:26:41Z leonardo $ */
 
15
/* chunk consolidating wrapper on a base memory allocator */
 
16
 
 
17
#include "memory_.h"
 
18
#include "gx.h"
 
19
#include "gsstype.h"
 
20
#include "gserrors.h"
 
21
#include "gsmchunk.h"
 
22
 
 
23
/* Raw memory procedures */
 
24
static gs_memory_proc_alloc_bytes(chunk_alloc_bytes_immovable);
 
25
static gs_memory_proc_resize_object(chunk_resize_object);
 
26
static gs_memory_proc_free_object(chunk_free_object);
 
27
static gs_memory_proc_stable(chunk_stable);
 
28
static gs_memory_proc_status(chunk_status);
 
29
static gs_memory_proc_free_all(chunk_free_all);
 
30
static gs_memory_proc_consolidate_free(chunk_consolidate_free);
 
31
 
 
32
/* Object memory procedures */
 
33
static gs_memory_proc_alloc_bytes(chunk_alloc_bytes);
 
34
static gs_memory_proc_alloc_struct(chunk_alloc_struct);
 
35
static gs_memory_proc_alloc_struct(chunk_alloc_struct_immovable);
 
36
static gs_memory_proc_alloc_byte_array(chunk_alloc_byte_array);
 
37
static gs_memory_proc_alloc_byte_array(chunk_alloc_byte_array_immovable);
 
38
static gs_memory_proc_alloc_struct_array(chunk_alloc_struct_array);
 
39
static gs_memory_proc_alloc_struct_array(chunk_alloc_struct_array_immovable);
 
40
static gs_memory_proc_object_size(chunk_object_size);
 
41
static gs_memory_proc_object_type(chunk_object_type);
 
42
static gs_memory_proc_alloc_string(chunk_alloc_string);
 
43
static gs_memory_proc_alloc_string(chunk_alloc_string_immovable);
 
44
static gs_memory_proc_resize_string(chunk_resize_string);
 
45
static gs_memory_proc_free_string(chunk_free_string);
 
46
static gs_memory_proc_register_root(chunk_register_root);
 
47
static gs_memory_proc_unregister_root(chunk_unregister_root);
 
48
static gs_memory_proc_enable_free(chunk_enable_free);
 
49
static const gs_memory_procs_t chunk_procs =
 
50
{
 
51
    /* Raw memory procedures */
 
52
    chunk_alloc_bytes_immovable,
 
53
    chunk_resize_object,
 
54
    chunk_free_object,
 
55
    chunk_stable,
 
56
    chunk_status,
 
57
    chunk_free_all,
 
58
    chunk_consolidate_free,
 
59
    /* Object memory procedures */
 
60
    chunk_alloc_bytes,
 
61
    chunk_alloc_struct,
 
62
    chunk_alloc_struct_immovable,
 
63
    chunk_alloc_byte_array,
 
64
    chunk_alloc_byte_array_immovable,
 
65
    chunk_alloc_struct_array,
 
66
    chunk_alloc_struct_array_immovable,
 
67
    chunk_object_size,
 
68
    chunk_object_type,
 
69
    chunk_alloc_string,
 
70
    chunk_alloc_string_immovable,
 
71
    chunk_resize_string,
 
72
    chunk_free_string,
 
73
    chunk_register_root,
 
74
    chunk_unregister_root,
 
75
    chunk_enable_free
 
76
};
 
77
 
 
78
typedef struct chunk_obj_node_s {
 
79
    struct chunk_obj_node_s *next;
 
80
    uint size;                  /* objlist: client size */
 
81
        /* if freelist: size of block (obj header and client area must fit in block) */
 
82
    gs_memory_type_ptr_t type;
 
83
#ifdef DEBUG
 
84
    unsigned long sequence;
 
85
#endif
 
86
} chunk_obj_node_t;
 
87
 
 
88
/*
 
89
 * Note: All objects within a chunk are 'aligned' since we round_up_to_align
 
90
 * the free list pointer when removing part of a free area.
 
91
 */
 
92
typedef struct chunk_mem_node_s {
 
93
    uint size;
 
94
    uint largest_free;          /* quick check when allocating */
 
95
    struct chunk_mem_node_s *next;
 
96
    chunk_obj_node_t *objlist;  /* head of objects in this chunk (no order) */
 
97
    chunk_obj_node_t *freelist;         /* free list (ordered) */
 
98
    /* chunk data follows immediately */
 
99
} chunk_mem_node_t;
 
100
 
 
101
typedef struct gs_memory_chunk_s {
 
102
    gs_memory_common;           /* interface outside world sees */
 
103
    gs_memory_t *target;        /* base allocator */
 
104
    chunk_mem_node_t *head_chunk;
 
105
#ifdef DEBUG
 
106
    unsigned long sequence_counter;
 
107
#endif
 
108
} gs_memory_chunk_t;
 
109
 
 
110
/* ---------- Public constructors/destructors ---------- */
 
111
 
 
112
/* Initialize a gs_memory_chunk_t */
 
113
int                             /* -ve error code or 0 */
 
114
gs_memory_chunk_wrap( gs_memory_t **wrapped,    /* chunk allocator init */
 
115
                      gs_memory_t * target )    /* base allocator */
 
116
{
 
117
    /* Use the non-GC allocator of the target. The chunk allocator is NOT GC safe */
 
118
    gs_memory_t *non_gc_target = target->non_gc_memory;
 
119
    gs_memory_chunk_t *cmem = NULL;
 
120
 
 
121
    *wrapped = NULL;            /* don't leave garbage in case we fail */
 
122
    if (non_gc_target) 
 
123
            cmem = (gs_memory_chunk_t *) gs_alloc_bytes_immovable(non_gc_target,
 
124
                        sizeof(gs_memory_chunk_t), "gs_malloc_wrap(chunk)");
 
125
    if (cmem == 0)
 
126
        return_error(gs_error_VMerror);
 
127
    cmem->stable_memory = (gs_memory_t *)cmem;  /* we are stable */
 
128
    cmem->procs = chunk_procs;
 
129
    cmem->gs_lib_ctx = non_gc_target->gs_lib_ctx;
 
130
    cmem->non_gc_memory = (gs_memory_t *)cmem;  /* and are not subject to GC */
 
131
    cmem->target = non_gc_target;
 
132
    cmem->head_chunk = NULL;
 
133
#ifdef DEBUG
 
134
    cmem->sequence_counter = 0;
 
135
#endif
 
136
 
 
137
    /* Init the chunk management values */
 
138
 
 
139
    *wrapped = (gs_memory_t *)cmem;
 
140
    return 0;
 
141
}
 
142
 
 
143
/* Release a chunk memory manager. */
 
144
/* Note that this has no effect on the target. */
 
145
void
 
146
gs_memory_chunk_release(gs_memory_t *mem)
 
147
{
 
148
    gs_memory_free_all((gs_memory_t *)mem, FREE_ALL_EVERYTHING,
 
149
                       "gs_memory_chunk_release");
 
150
}
 
151
 
 
152
/* ---------- Accessors ------------- */
 
153
 
 
154
/* Retrieve this allocator's target */
 
155
gs_memory_t *
 
156
gs_memory_chunk_target(const gs_memory_t *mem)
 
157
{
 
158
    gs_memory_chunk_t *cmem = (gs_memory_chunk_t *)mem;
 
159
    return cmem->target;
 
160
}
 
161
 
 
162
#ifdef DEBUG
 
163
void
 
164
gs_memory_chunk_dump_memory(const gs_memory_t *mem)
 
165
{
 
166
    gs_memory_chunk_t *cmem = (gs_memory_chunk_t *)mem;
 
167
    chunk_mem_node_t *head = cmem->head_chunk;
 
168
    chunk_mem_node_t *current;
 
169
    chunk_mem_node_t *next;
 
170
 
 
171
    current = head;
 
172
    while ( current != NULL ) { 
 
173
        if (current->objlist != NULL) {
 
174
            chunk_obj_node_t *obj;
 
175
 
 
176
            for (obj= current->objlist; obj != NULL; obj=obj->next) 
 
177
                dprintf4("chunk_mem leak, obj=0x%lx, size=%d, type=%s, sequence#=%ld\n",
 
178
                        (ulong)obj, obj->size, obj->type->sname, obj->sequence);
 
179
        }
 
180
        next = current->next; 
 
181
        current = next;
 
182
    }
 
183
}
 
184
#endif
 
185
 
 
186
/* -------- Private members --------- */
 
187
 
 
188
/* Note that all of the data is 'immovable' and is opaque to the base allocator */
 
189
/* thus even if it is a GC type of allocator, no GC functions will be applied   */
 
190
/* All allocations are done in the non_gc_memory of the base */
 
191
 
 
192
/* Procedures */
 
193
 
 
194
static void
 
195
chunk_mem_node_free_all_remaining(gs_memory_chunk_t *cmem)
 
196
{
 
197
    chunk_mem_node_t *head = cmem->head_chunk;
 
198
    gs_memory_t * const target = cmem->target;
 
199
    chunk_mem_node_t *current;
 
200
    chunk_mem_node_t *next;
 
201
 
 
202
    current = head;
 
203
    while ( current != NULL ) { 
 
204
        next = current->next; 
 
205
        gs_free_object(target, current, "chunk_mem_node_remove");
 
206
        current = next;
 
207
    }
 
208
    cmem->head_chunk = NULL;
 
209
}
 
210
 
 
211
static void
 
212
chunk_free_all(gs_memory_t * mem, uint free_mask, client_name_t cname)
 
213
{
 
214
    gs_memory_chunk_t * const cmem = (gs_memory_chunk_t *)mem;
 
215
    gs_memory_t * const target = cmem->target;
 
216
 
 
217
    /* Only free the structures and the allocator itself. */
 
218
    if (mem->stable_memory) {
 
219
        if (mem->stable_memory != mem)
 
220
            gs_memory_free_all(mem->stable_memory, free_mask, cname);
 
221
        if (free_mask & FREE_ALL_ALLOCATOR)
 
222
            mem->stable_memory = 0;
 
223
    }
 
224
    if (free_mask & FREE_ALL_DATA) {
 
225
        chunk_mem_node_free_all_remaining(cmem);
 
226
    }
 
227
    if (free_mask & FREE_ALL_STRUCTURES) {
 
228
        cmem->target = 0;
 
229
    }
 
230
    if (free_mask & FREE_ALL_ALLOCATOR)
 
231
        gs_free_object(target, cmem, cname);
 
232
}
 
233
 
 
234
extern const gs_memory_struct_type_t st_bytes;
 
235
 
 
236
/* round up objects to make sure we have room for a header left */
 
237
inline static uint
 
238
round_up_to_align(uint size)
 
239
{
 
240
    uint num_node_headers = (size + sizeof(chunk_obj_node_t) - 1) / sizeof(chunk_obj_node_t);
 
241
 
 
242
    return num_node_headers * sizeof(chunk_obj_node_t);
 
243
}
 
244
 
 
245
#define IS_SINGLE_OBJ_SIZE(chunk_size) \
 
246
    (chunk_size > (CHUNK_SIZE>>1))
 
247
#define MULTIPLE_OBJ_CHUNK_SIZE \
 
248
    (sizeof(chunk_mem_node_t) + round_up_to_align(CHUNK_SIZE))
 
249
 
 
250
 
 
251
/* return -1 on error, 0 on success */
 
252
static int 
 
253
chunk_mem_node_add(gs_memory_chunk_t *cmem, uint size_needed, chunk_mem_node_t **newchunk)
 
254
{
 
255
    chunk_mem_node_t *node, *prev_node;
 
256
    gs_memory_t *target = cmem->target;
 
257
    /* Allocate enough for the chunk header, and the size_needed */
 
258
    /* The size needed already includes the object header from caller */
 
259
    /* and is already rounded up to the obj_node_t sized elements */
 
260
    uint chunk_size = size_needed + sizeof(chunk_mem_node_t);
 
261
    bool is_multiple_object_node = false;
 
262
 
 
263
    /* Objects > half the default chunk size get their own chunk */
 
264
    if ( ! IS_SINGLE_OBJ_SIZE(chunk_size)) {
 
265
        chunk_size = MULTIPLE_OBJ_CHUNK_SIZE;   /* the size for collections of objects */
 
266
        is_multiple_object_node = true;
 
267
    }
 
268
 
 
269
    *newchunk = NULL;
 
270
    node = (chunk_mem_node_t *)gs_alloc_bytes_immovable(target, chunk_size, "chunk_mem_node_add");
 
271
    if ( node == NULL )
 
272
        return -1;
 
273
    node->size = chunk_size;    /* how much we allocated */
 
274
    node->largest_free = chunk_size - sizeof(chunk_mem_node_t);
 
275
    node->objlist = NULL;
 
276
    node->freelist = (chunk_obj_node_t *)((byte *)(node) + sizeof(chunk_mem_node_t));
 
277
    node->freelist->next = NULL;
 
278
    node->freelist->size = node->largest_free;
 
279
 
 
280
    prev_node = NULL;
 
281
    if (!is_multiple_object_node) {
 
282
        chunk_mem_node_t *scan_node;
 
283
 
 
284
        /* Scan past chunks that are collections of smaller chunks */
 
285
        /* This allows the most frequently accessed chunks to be near the head */
 
286
        for (scan_node = cmem->head_chunk; scan_node != NULL; scan_node = scan_node->next) {
 
287
            if (scan_node->size != MULTIPLE_OBJ_CHUNK_SIZE)
 
288
                break;
 
289
            prev_node = scan_node;
 
290
        }
 
291
    }
 
292
    if (prev_node == NULL) {
 
293
        if (cmem->head_chunk == NULL) {
 
294
            cmem->head_chunk = node;
 
295
            node->next = NULL;
 
296
        } else {
 
297
            node->next = cmem->head_chunk;
 
298
            cmem->head_chunk = node;
 
299
        }
 
300
    } else {
 
301
        node->next = prev_node->next;
 
302
        prev_node->next = node;
 
303
    }
 
304
 
 
305
    *newchunk = node;       /* return the chunk we just allocated */
 
306
    return 0;
 
307
}
 
308
 
 
309
static int        
 
310
chunk_mem_node_remove(gs_memory_chunk_t *cmem, chunk_mem_node_t *addr)
 
311
{
 
312
    chunk_mem_node_t *head = cmem->head_chunk;
 
313
    gs_memory_t * const target = cmem->target;
 
314
 
 
315
    /* check the head first */
 
316
    if (head == NULL) {
 
317
        dprintf("FAIL - no nodes to be removed\n" );
 
318
        return -1;
 
319
    }
 
320
    if (head == addr) {
 
321
        cmem->head_chunk = head->next;
 
322
        gs_free_object(target, head, "chunk_mem_node_remove");
 
323
    } else {
 
324
        chunk_mem_node_t *current;
 
325
        bool found = false;
 
326
 
 
327
        /* scan the list, stopping in front of element */
 
328
        for (current = head; current != NULL; current = current->next) {
 
329
            if ( current->next && (current->next == addr) ) {
 
330
                current->next = current->next->next;    /* de-link it */
 
331
                gs_free_object(target, addr, "chunk_mem_node_remove");
 
332
                found = true;
 
333
                break;
 
334
            }
 
335
        }
 
336
        if ( !found ) {
 
337
            dprintf1("FAIL freeing wild pointer freed address 0x%lx not found\n", (ulong)addr );
 
338
            return -1;
 
339
        }
 
340
    }
 
341
    return 0;
 
342
}
 
343
    
 
344
/* all of the allocation routines reduce to the this function */
 
345
static byte *
 
346
chunk_obj_alloc(gs_memory_t *mem, uint size, gs_memory_type_ptr_t type, client_name_t cname)
 
347
{
 
348
    gs_memory_chunk_t *cmem = (gs_memory_chunk_t *)mem;
 
349
    chunk_mem_node_t *head = cmem->head_chunk;
 
350
    uint newsize, free_size;
 
351
    chunk_obj_node_t *newobj = NULL;
 
352
    chunk_obj_node_t *free_obj, *prev_free, *new_free;
 
353
    chunk_mem_node_t *current;
 
354
    bool rescan_free_list;
 
355
 
 
356
    newsize = round_up_to_align(size + sizeof(chunk_obj_node_t));       /* space we will need */
 
357
    
 
358
    /* Search the chunks for one with a large enough free area */
 
359
    for (current = head; current != NULL; current = current->next) {
 
360
        if ( current->largest_free >= newsize) 
 
361
            break;
 
362
    }
 
363
    if (current == NULL) {
 
364
        /* No chunks with enough space, allocate one */
 
365
        if (chunk_mem_node_add(cmem, newsize, &current) < 0)
 
366
            return NULL;
 
367
    }
 
368
    /* Find the first free area in the current chunk that is big enough */
 
369
    /* LATER: might be better to find the 'best fit' */
 
370
    prev_free = NULL;           /* NULL means chunk */
 
371
    for (free_obj = current->freelist; free_obj != NULL; free_obj=free_obj->next) {
 
372
        if (free_obj->size >= newsize) 
 
373
            break;
 
374
        prev_free = free_obj;   /* keep track so we can update link */
 
375
    }
 
376
 
 
377
    if (free_obj == NULL) {
 
378
        dprintf2("largest_free value = %d is too large, cannot find room for size = %d\n",
 
379
            current->largest_free, newsize);
 
380
        return NULL;
 
381
    }
 
382
 
 
383
    /* If this free object's size == largest_free, we'll have to re-scan */
 
384
    rescan_free_list = free_obj->size == current->largest_free;
 
385
 
 
386
    /* Make an object in the free_obj we found above, reducing it's size */
 
387
    /* and adjusting the free list preserving alignment */
 
388
    newobj = free_obj;
 
389
    free_size = free_obj->size - newsize;       /* amount remaining */
 
390
    new_free = (chunk_obj_node_t *)((byte *)(free_obj) + newsize);      /* start of remaining free area */
 
391
    if (free_size >= sizeof(chunk_obj_node_t)) {
 
392
        if (prev_free != NULL)
 
393
            prev_free->next = new_free;
 
394
        else
 
395
            current->freelist = new_free;
 
396
        new_free->next = free_obj->next;
 
397
        new_free->size = free_size;
 
398
    } else {
 
399
       /* Not enough space remaining, just skip around it */
 
400
        if (prev_free != NULL)
 
401
            prev_free->next = free_obj->next;
 
402
        else
 
403
            current->freelist = free_obj->next;
 
404
    }
 
405
 
 
406
#ifdef DEBUG
 
407
    memset((byte *)(newobj) + sizeof(chunk_obj_node_t), 0xa1, newsize - sizeof(chunk_obj_node_t));
 
408
    memset((byte *)(newobj) + sizeof(chunk_obj_node_t), 0xac, size);
 
409
    newobj->sequence = cmem->sequence_counter++;
 
410
#endif
 
411
 
 
412
    newobj->next = current->objlist;    /* link to start of list */
 
413
    current->objlist = newobj;
 
414
    newobj->size = size;                /* client requested size */
 
415
    newobj->type = type;                /* and client desired type */
 
416
 
 
417
    /* If we flagged for re-scan to find the new largest_free, do it now */
 
418
    if (rescan_free_list) {
 
419
        current->largest_free = 0;
 
420
        for (free_obj = current->freelist; free_obj != NULL; free_obj=free_obj->next)
 
421
            if (free_obj->size > current->largest_free) 
 
422
                current->largest_free = free_obj->size;
 
423
    }
 
424
 
 
425
    /* return the client area of the object we allocated */
 
426
    return (byte *)(newobj) + sizeof(chunk_obj_node_t);
 
427
}
 
428
 
 
429
static byte *
 
430
chunk_alloc_bytes_immovable(gs_memory_t * mem, uint size, client_name_t cname)
 
431
{
 
432
    return chunk_obj_alloc(mem, size, &st_bytes, cname);
 
433
}
 
434
 
 
435
static byte *
 
436
chunk_alloc_bytes(gs_memory_t * mem, uint size, client_name_t cname)
 
437
{
 
438
    return chunk_obj_alloc(mem, size, &st_bytes, cname);
 
439
}
 
440
 
 
441
static void *
 
442
chunk_alloc_struct_immovable(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
 
443
                         client_name_t cname)
 
444
{
 
445
    return chunk_obj_alloc(mem, pstype->ssize, pstype, cname);
 
446
}
 
447
 
 
448
static void *
 
449
chunk_alloc_struct(gs_memory_t * mem, gs_memory_type_ptr_t pstype,
 
450
               client_name_t cname)
 
451
{
 
452
    return chunk_obj_alloc(mem, pstype->ssize, pstype, cname);
 
453
}
 
454
 
 
455
static byte *
 
456
chunk_alloc_byte_array_immovable(gs_memory_t * mem, uint num_elements,
 
457
                             uint elt_size, client_name_t cname)
 
458
{
 
459
    return chunk_alloc_bytes(mem, num_elements * elt_size, cname);
 
460
}
 
461
 
 
462
static byte *
 
463
chunk_alloc_byte_array(gs_memory_t * mem, uint num_elements, uint elt_size,
 
464
                   client_name_t cname)
 
465
{
 
466
    return chunk_alloc_bytes(mem, num_elements * elt_size, cname);
 
467
}
 
468
 
 
469
static void *
 
470
chunk_alloc_struct_array_immovable(gs_memory_t * mem, uint num_elements,
 
471
                           gs_memory_type_ptr_t pstype, client_name_t cname)
 
472
{
 
473
    return chunk_obj_alloc(mem, num_elements * pstype->ssize, pstype, cname);
 
474
}
 
475
 
 
476
static void *
 
477
chunk_alloc_struct_array(gs_memory_t * mem, uint num_elements,
 
478
                     gs_memory_type_ptr_t pstype, client_name_t cname)
 
479
{
 
480
    return chunk_obj_alloc(mem, num_elements * pstype->ssize, pstype, cname);
 
481
}
 
482
 
 
483
 
 
484
static void *
 
485
chunk_resize_object(gs_memory_t * mem, void *ptr, uint new_num_elements, client_name_t cname)
 
486
{
 
487
    /* get the type from the old object */
 
488
    chunk_obj_node_t *obj = ((chunk_obj_node_t *)ptr) - 1;
 
489
    uint new_size = (obj->type->ssize * new_num_elements);
 
490
 
 
491
    /* This isn't particularly efficient, but it is rarely used */
 
492
    chunk_free_object(mem, ptr, cname);
 
493
    return chunk_obj_alloc(mem, new_size, obj->type, cname);
 
494
}
 
495
        
 
496
static void
 
497
chunk_free_object(gs_memory_t * mem, void *ptr, client_name_t cname)
 
498
{
 
499
    if (ptr == NULL ) 
 
500
        return;
 
501
    {
 
502
        /* back up to obj header */
 
503
        chunk_obj_node_t *obj = ((chunk_obj_node_t *)ptr) - 1;
 
504
        void (*finalize)(void *ptr) = obj->type->finalize;
 
505
        gs_memory_chunk_t * const cmem = (gs_memory_chunk_t *)mem;
 
506
        chunk_mem_node_t *current;
 
507
        chunk_obj_node_t *free_obj, *prev_free;
 
508
        chunk_obj_node_t *scan_obj, *prev_obj;
 
509
        /* space we will free */
 
510
        uint freed_size = round_up_to_align(obj->size + sizeof(chunk_obj_node_t));
 
511
 
 
512
        if ( finalize != NULL )
 
513
            finalize(ptr);
 
514
 
 
515
        /* Find the chunk containing this object */
 
516
        for (current = cmem->head_chunk; current != NULL; current = current->next) {
 
517
            if (((byte *)obj > (byte *)current) && ((byte *)obj < (byte *)(current) + current->size))
 
518
                break;
 
519
        }
 
520
        if (current == NULL) {
 
521
            /* Object not found in any chunk */
 
522
            dprintf1("chunk_free_obj failed, object 0x%lx not in any chunk\n", ((ulong)obj));
 
523
            return;
 
524
        }
 
525
 
 
526
        /* Scan obj list to find this element */
 
527
        prev_obj = NULL;        /* object is head, linked to mem node */
 
528
        for (scan_obj = current->objlist; scan_obj != NULL; scan_obj = scan_obj->next) {
 
529
            if (scan_obj == obj)
 
530
                break;
 
531
            prev_obj = scan_obj;
 
532
        }
 
533
        if (scan_obj == NULL) {
 
534
            /* Object not found in expected chunk */
 
535
            dprintf3("chunk_free_obj failed, object 0x%lx not in chunk at 0x%lx, size = %d\n",
 
536
                            ((ulong)obj), ((ulong)current), current->size);
 
537
            return;
 
538
        }
 
539
        /* link around the object being freed */
 
540
        if (prev_obj == NULL)
 
541
            current->objlist = obj->next;
 
542
        else 
 
543
            prev_obj->next = obj->next;
 
544
 
 
545
        /* Add this object's space (including the header) to the free list */
 
546
 
 
547
        /* Scan free list to find where this element goes */
 
548
        obj->size = freed_size;     /* adjust size to include chunk_obj_node and pad */
 
549
 
 
550
        prev_free = NULL;
 
551
        for (free_obj = current->freelist; free_obj != NULL; free_obj = free_obj->next) {
 
552
            if (obj < free_obj)
 
553
                break;
 
554
            prev_free = free_obj;
 
555
        }
 
556
        if (prev_free == NULL) {
 
557
            /* this object is before any other free objects */
 
558
            obj->next = current->freelist;
 
559
            current->freelist = obj;
 
560
        } else {
 
561
            obj->next = free_obj;
 
562
            prev_free->next = obj;
 
563
        }
 
564
        /* If the end of this object is adjacent to the next free space,
 
565
         * merge the two. Next we'll merge with predecessor (prev_free)
 
566
         */
 
567
        if (free_obj != NULL) {
 
568
            byte *after_obj = (byte*)(obj) + freed_size;
 
569
            
 
570
            if (free_obj <= (chunk_obj_node_t *)after_obj) {
 
571
                /* Object is adjacent to following free space block -- merge it */
 
572
                obj->next = free_obj->next;     /* link around the one being absorbed */
 
573
                obj->size = (byte *)(free_obj) - (byte *)(obj) + free_obj->size;
 
574
            }
 
575
        }
 
576
        /* the prev_free object precedes this object that is now free,
 
577
         * it _may_ be adjacent
 
578
         */
 
579
        if (prev_free != NULL) {
 
580
            byte *after_free = (byte*)(prev_free) + prev_free->size;
 
581
            
 
582
            if (obj <= (chunk_obj_node_t *)after_free) {
 
583
                /* Object is adjacent to prior free space block -- merge it */
 
584
                /* NB: this is the common case with LIFO alloc-free patterns */
 
585
                /* (LIFO: Last-allocated, first freed) */
 
586
                prev_free->size = (byte *)(obj) - (byte *)(prev_free) + obj->size;
 
587
                prev_free->next = obj->next;            /* link around 'obj' area */
 
588
                obj = prev_free;
 
589
            }
 
590
        }
 
591
#ifdef DEBUG
 
592
memset((byte *)(obj) + sizeof(chunk_obj_node_t), 0xf1, obj->size - sizeof(chunk_obj_node_t));
 
593
#endif
 
594
        if (current->largest_free < obj->size)
 
595
            current->largest_free = obj->size;
 
596
 
 
597
        /* If this chunk is now totally empty, free it */
 
598
        if (current->objlist == NULL) {
 
599
            if (current->size != current->freelist->size + sizeof(chunk_mem_node_t))
 
600
                dprintf2("chunk freelist size not correct, is: %d, should be: %d\n",
 
601
                    round_up_to_align(current->freelist->size + sizeof(chunk_mem_node_t)), current->size);
 
602
            chunk_mem_node_remove(cmem, current);
 
603
        }
 
604
    }
 
605
}
 
606
 
 
607
static byte *
 
608
chunk_alloc_string_immovable(gs_memory_t * mem, uint nbytes, client_name_t cname)
 
609
{
 
610
    /* we just alloc bytes here */
 
611
    return chunk_alloc_bytes(mem, nbytes, cname);
 
612
}
 
613
 
 
614
static byte *
 
615
chunk_alloc_string(gs_memory_t * mem, uint nbytes, client_name_t cname)
 
616
{
 
617
    /* we just alloc bytes here */
 
618
    return chunk_alloc_bytes(mem, nbytes, cname);
 
619
}
 
620
 
 
621
static byte *
 
622
chunk_resize_string(gs_memory_t * mem, byte * data, uint old_num, uint new_num,
 
623
                client_name_t cname)
 
624
{
 
625
    /* just resize object - ignores old_num */
 
626
    return chunk_resize_object(mem, data, new_num, cname);
 
627
}
 
628
 
 
629
static void
 
630
chunk_free_string(gs_memory_t * mem, byte * data, uint nbytes,
 
631
              client_name_t cname)
 
632
{
 
633
    chunk_free_object(mem, data, cname);
 
634
}
 
635
 
 
636
static void
 
637
chunk_status(gs_memory_t * mem, gs_memory_status_t * pstat)
 
638
{
 
639
}
 
640
 
 
641
static gs_memory_t *
 
642
chunk_stable(gs_memory_t * mem)
 
643
{
 
644
    return mem;
 
645
}
 
646
 
 
647
static void
 
648
chunk_enable_free(gs_memory_t * mem, bool enable)
 
649
{
 
650
}
 
651
 
 
652
static void
 
653
chunk_consolidate_free(gs_memory_t *mem)
 
654
{
 
655
}
 
656
 
 
657
/* aceesors to get size and type given the pointer returned to the client */
 
658
static uint
 
659
chunk_object_size(gs_memory_t * mem, const void *ptr)
 
660
{
 
661
    chunk_obj_node_t *obj = ((chunk_obj_node_t *)ptr) - 1;
 
662
 
 
663
    return obj->size;
 
664
}
 
665
 
 
666
static gs_memory_type_ptr_t
 
667
chunk_object_type(const gs_memory_t * mem, const void *ptr)
 
668
{
 
669
    chunk_obj_node_t *obj = ((chunk_obj_node_t *)ptr) - 1;
 
670
    return obj->type;
 
671
}
 
672
 
 
673
static int
 
674
chunk_register_root(gs_memory_t * mem, gs_gc_root_t * rp, gs_ptr_type_t ptype,
 
675
                 void **up, client_name_t cname)
 
676
{
 
677
    return 0;
 
678
}
 
679
 
 
680
static void
 
681
chunk_unregister_root(gs_memory_t * mem, gs_gc_root_t * rp, client_name_t cname)
 
682
{
 
683
}
 
684
 
 
685
#ifdef DEBUG
 
686
 
 
687
#define A(obj, size) \
 
688
    if ((obj = gs_alloc_bytes(cmem, size, "chunk_alloc_unit_test")) == NULL) { \
 
689
        dprintf("chunk alloc failed\n"); \
 
690
        return_error(gs_error_VMerror); \
 
691
    }
 
692
 
 
693
#define F(obj) \
 
694
    gs_free_object(cmem, obj, "chunk_alloc_unit_test");
 
695
 
 
696
int
 
697
chunk_allocator_unit_test(gs_memory_t *mem)
 
698
{
 
699
    int code;
 
700
    gs_memory_t *cmem;
 
701
    byte *obj1, *obj2, *obj3, *obj4, *obj5, *obj6, *obj7; 
 
702
 
 
703
    if ((code = gs_memory_chunk_wrap(&cmem, mem )) < 0) {
 
704
        dprintf1("chunk_wrap returned error code: %d\n", code);
 
705
        return code;
 
706
    }
 
707
 
 
708
    /* Allocate a large object */
 
709
    A(obj1, 80000);
 
710
    F(obj1);
 
711
    A(obj1, 80000);
 
712
 
 
713
    A(obj2, 3);
 
714
    A(obj3, 7);
 
715
    A(obj4, 15);
 
716
    A(obj5, 16);
 
717
    A(obj6, 16);
 
718
    A(obj7, 16);
 
719
 
 
720
    F(obj2);
 
721
    F(obj1);
 
722
    F(obj5);
 
723
    F(obj4);
 
724
    F(obj6);
 
725
    F(obj7);
 
726
    F(obj3);
 
727
 
 
728
    /* cleanup */
 
729
    gs_memory_chunk_release(cmem);
 
730
    return 0;
 
731
}
 
732
 
 
733
#endif /* DEBUG */