source: branches/ithildin-1.1/source/malloc.c @ 589

Revision 589, 26.6 KB checked in by wd, 7 years ago (diff)
  • Fix more warnings.
  • Add rlimit changing code.
  • Property svn:keywords set to Id Rev
Line 
1/*
2 * malloc.c: optimised memory allocation routines
3 *
4 * Copyright 2003 the Ithildin Project.
5 * See the COPYING file for more information on licensing and use.
6 *
7 * this is an evolved version of my previous 'blockheap' allocator.  this is
8 * intended to replace malloc in all calls and makes (hopefully) intelligent
9 * decisions about how to allocate chunks of memory.  it is optimised for the
10 * "thousands or more allocations of same-sized memory" case.
11 */
12
13#include <ithildin/stand.h>
14#ifdef USE_INTERNAL_MALLOC
15
16/* some options for the memory allocator.  tweaking these might be useful for
17 * performance optimisation. */
18
19/* the 'fuzziness' of allocated memory.  this is the factor by which memory
20 * heaps are increased. */
21#define MALLOC_FUZZ 16
22/* the minimum memory size to allow allocations for.  setting it to MALLOC_FUZZ
23 * is probably a fairly safe idea */
24#define MALLOC_MIN_SIZE MALLOC_FUZZ
25/* the maximum size (in terms of elements) to grab from the system.
26 * allocation sizes begin small and ramp up to meet demand. */
27#define MALLOC_MAX_ELEMS 1024
28/* the maximum number of pages to get from the system.  this takes precedence
29 * over MALLOC_MAX_ELEMS */
30#define MALLOC_MAX_PAGES 64
31/* the maximum size of allocation to handle.  allocations for memory larger
32 * than this will always be one page or more because at this size it becomes
33 * inefficient to try and wrangle these allocations into being any less
34 * wasteful than that */
35#define MALLOC_MAX_SIZE (MALLOC_PAGESIZE / 2)
36/* number of blocks to 'cache' in the system.  if more blocks than this are
37 * free the library will return the pages to the system. */
38#define MALLOC_CACHE_BLOCKS 1
39/* location of the configuration file we use. */
40#define MALLOC_CONFIG_FILE "/etc/malloc.conf"
41
42/* this structure contains the malloc options which are runtime tuneable.  the
43 * runtime settings can be gotten from several places: a system file, an
44 * environment variable, and a variable defined in the program.  this concept
45 * was borrowed from Poul-Henning Kamp's malloc system, and in fact several of
46 * the options in his allocator map to the options in this allocator. */
47static struct {
48    bool    wfatal;                /* warnings become fatal */
49    bool    fill;                /* fill memory with 'fbyte' when allocated and
50                                   freed. */
51    char    fbyte;
52    bool    force_realloc;        /* force a memory re-allocation even if the
53                                   memory's current area is large enough to
54                                   hold the new data without one */
55    bool    sysv_compat;        /* 0-byte allocation returns NULL? */
56    bool    nomem_fatal;        /* abort() if no memory is available. */
57    int            page_cache;                /* number of pages to cache at the end of
58                                   memory before returning memory to the
59                                   system. */
60    int            block_cache;        /* number of empty blocks to cache for each
61                                   heap. <1 forces immediate return of these
62                                   blocks to the system. */
63    bool    sanity;                /* do extra sanity checking to better handle
64                                   corrupted memory */
65    bool    optimize;           /* optimize with "look-ahead" allocations */
66} options;
67
68static inline void malloc_debug(char *, ...);
69static inline void malloc_error(char *, ...);
70static inline void malloc_warn(char *, ...);
71
72/*****************************************************************************
73 * low-level memory management code                                             *
74 *****************************************************************************/
75/* this is the low-level malloc system code.  this code is used to track pages
76 * used by malloc and to gain memory for the block structures.  further down is
77 * the actual malloc code. */
78/* this is the minimum number of pages we will ask the system for when
79 * increasing the program's brk section */
80#define MALLOC_PAGE_MINALLOC 16
81/* this is the number of free pages we will cache *at the end of the heap area*
82 * when looking to return data back to the system */
83#define MALLOC_PAGE_CACHE MALLOC_PAGE_MINALLOC
84
85/* flags in the page listing.  each page is represented by a single byte in an
86 * array.  initially the array is allocated to handle MALLOC_PAGESIZE pages via
87 * mmap.  we also store the offset of the first free page in the count so that
88 * we can find free pages somewhat more quickly. */
89#define MALLOC_PAGE_OTHER        0
90#define MALLOC_PAGE_FREE        0x01
91#define MALLOC_PAGE_BALLOC        0x02
92#define MALLOC_PAGE_ALLOC        0x04
93#define MALLOC_PAGE_START        (MALLOC_PAGE_BALLOC | MALLOC_PAGE_ALLOC)
94#define MALLOC_PAGE_CONTINUED        0x08
95static struct {
96    char *start;                        /* start of memory */
97    char *end;                                /* end of memory */
98
99    char *parray;                        /* array of page descriptions */
100    size_t size;                        /* size of the page array */
101} page_desc;
102
103/* find the next page boundary after 'addr'.  if addr is page-aligned then it
104 * is returned unmodified. */
105#define next_page_boundary(addr) ((long)(addr) % MALLOC_PAGESIZE != 0 ?        \
106        (addr) + (MALLOC_PAGESIZE - (long)(addr) % MALLOC_PAGESIZE) : (addr))
107/* macro to get the address of the character in the page map for a certain
108 * address */
109#define mem_to_pindex(mem)                                                \
110    (((char *)(mem) - page_desc.start) / MALLOC_PAGESIZE)
111#define pindex_to_mem(idx) (page_desc.start + (idx * MALLOC_PAGESIZE))
112
113/* grow the heap by 'count' pages.  will return false if this fails */
114static bool grow_heap(int count) {
115    char *mem, *s;
116    unsigned int len;
117
118    mem = sbrk(0); /* see what our breakpoint is now... this may not be page
119                      aligned.. */
120    mem = next_page_boundary(mem);
121    page_desc.end = mem;
122
123    if (count < MALLOC_PAGE_MINALLOC)
124        count = MALLOC_PAGE_MINALLOC;
125
126    if (brk(page_desc.end + (count * MALLOC_PAGESIZE)) != 0)
127        return false;
128    page_desc.end += count * MALLOC_PAGESIZE;
129
130    /* we've added at least another 'len' pages to the map. we need to do some
131     * work to figure out if we need to increase the page array, and where it
132     * should be filled in (because other people may call brk() on us.) */
133    len = (page_desc.end - page_desc.start) / MALLOC_PAGESIZE;
134    if (len > page_desc.size) {
135        /* our array is too small, get a new one.. */
136        s = page_desc.parray;
137        if ((page_desc.parray = mmap(NULL, page_desc.size + MALLOC_PAGESIZE,
138                PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0)) == NULL) {
139            malloc_error("mmap(NULL, %d, ...): %s",
140                    page_desc.size + MALLOC_PAGESIZE, strerror(errno));
141        }
142        memset(page_desc.parray, MALLOC_PAGE_OTHER,
143                page_desc.size + MALLOC_PAGESIZE);
144        if (s != NULL) {
145            memcpy(page_desc.parray, s, page_desc.size);
146            munmap(s, page_desc.size);
147        }
148        page_desc.size += MALLOC_PAGESIZE;
149    }
150
151    /* set our newly snagged pages free */
152    s = page_desc.parray + ((mem - page_desc.start) / MALLOC_PAGESIZE);
153    memset(s, MALLOC_PAGE_FREE, count);
154
155    return true;
156}
157
158/* allocate 'count' pages.  for count>1 this gets increasingly expensive as we
159 * traverse the page array.  if we have no free space to satisfy the request
160 * then we increment the data size and return that memory */
161static void *alloc_pages(int count) {
162    int pages;
163    int idx = 0, eidx;
164
165    pages = (page_desc.end - page_desc.start) / MALLOC_PAGESIZE;
166    while (idx < pages) {
167        /* skip any non-free pages */
168        while (idx < pages && page_desc.parray[idx] != MALLOC_PAGE_FREE)
169            idx++;
170        if (idx == pages)
171            break;
172
173        /* now see if this region is large enough */
174        eidx = idx;
175        while (eidx < pages && page_desc.parray[eidx] == MALLOC_PAGE_FREE)
176            eidx++;
177        if (eidx - idx >= count) {
178            /* we have enough free pages, great!  mark them as allocated and
179             * then return the memory from the pages. */
180            page_desc.parray[idx] = MALLOC_PAGE_ALLOC;
181            while (--count != 0)
182                page_desc.parray[idx + count] = MALLOC_PAGE_CONTINUED;
183            return page_desc.start + (MALLOC_PAGESIZE * idx);
184        }
185        /* maybe try again.. (eidx will be the page after the last free group
186         * we found, or 'pages' */
187        idx = eidx;
188    }
189
190    /* okay, we couldn't find a free section above, guess we need to increase
191     * the break */
192    if (grow_heap(count))
193        return alloc_pages(count);
194    errno = ENOMEM;
195    return NULL;
196}
197
198static void free_pages(void *mem) {
199    unsigned int idx, eidx;
200
201    if ((long)mem % MALLOC_PAGESIZE != 0) {
202        malloc_warn("free_pages(%p): memory is not page-aligned?", mem);
203        return;
204    }
205
206    /* find out how many pages were allocated */
207    idx = mem_to_pindex(mem);
208    eidx = idx + 1;
209    while (page_desc.parray[eidx] == MALLOC_PAGE_CONTINUED &&
210            eidx < page_desc.size)
211        eidx++;
212    memset(page_desc.parray + idx, MALLOC_PAGE_FREE, eidx - idx);
213
214    /* see if there are pages at the end of the cache that can be given back to
215     * the system. */
216    idx = eidx = ((page_desc.end - page_desc.start) / MALLOC_PAGESIZE) - 1;
217    while (page_desc.parray[idx] == MALLOC_PAGE_FREE && idx > 0)
218        idx--;
219    if ((idx += options.page_cache) < eidx) {
220        /* some leftovers (we think).  we see if the break is what we think it
221         * is, and if it is, then we return this memory to the system */
222        mem = page_desc.start + (MALLOC_PAGESIZE * idx);
223        if (sbrk(0) == page_desc.end) {
224            malloc_debug("free_pages(): returning %d pages to the system",
225                    (eidx - idx));
226            if (brk(mem) != 0)
227                abort(); /* uh oh.. */
228            page_desc.end = mem;
229        }
230    }
231}
232
233/*****************************************************************************
234 * actual malloc code                                                             *
235 *****************************************************************************/
236/* the heap structure.  these are the top-level memory sources.  there is one
237 * for each allocation size available.  using the defaults from above this
238 * yields 47 separate heaps. */
239#define MALLOC_HEAP_COUNT                                                \
240(((MALLOC_MAX_SIZE - MALLOC_MIN_SIZE) / MALLOC_FUZZ) + 1)
241struct heap {
242    size_t size;                    /* size allocated to the heap */
243    int etotal;                     /* total element count */
244    int ealloc;                     /* allocated element count */
245
246    int blocks;                     /* 'blocks' of memory allocated */
247    int bpages;                     /* number of pages we're using for blocks, will
248                                       increase until bpages > MALLOC_MAX_PAGES or
249                                       the number of elements in a block would be >
250                                       MALLOC_MAX_ELEMS */
251    int bempty;                     /* blocks which are empty */
252    int bfull;                      /* blockx which are full */
253
254    int allocs;                     /* 'alloc' calls */
255    int frees;                      /* 'free' calls */
256
257    LIST_HEAD(, block) blist;       /* list of allocated blocks */
258    LIST_HEAD(, block) oblist;      /* list of blocks with some free memory */
259} heaps[MALLOC_HEAP_COUNT];
260
261/* a block is a region of contiguous memory.  the size of a block may vary from
262 * block to block, depending on how the heap allocator has tuned itself. */
263struct block {
264    struct heap *heap;              /* the heap this block belongs to */
265    char *start;                    /* start of memory */
266    char *end;                      /* end of memory.  this is the end in terms of
267                                       where the last *element* ends, there may be
268                                       unused memory beyond this point up to the
269                                       next page boundary. */
270    unsigned int used;              /* number of used elements */
271    SLIST_HEAD(, block_elem) elist;
272
273    LIST_ENTRY(block) lp;
274    LIST_ENTRY(block) olp;
275};
276#define elems_in_block(block)                                                \
277(((block)->end - (block)->start) / (block)->heap->size)
278
279/* this is a single element in a block.  block elements are stored in a singly
280 * linked list for quick allocation */
281struct block_elem {
282#define MALLOC_MAGIC (uint32_t)0xdeadc0de
283    uint32_t magic;                /* the magic number.  set when this block
284                                   should no longer be touched */
285    SLIST_ENTRY(block_elem) lp;
286};
287
288/*****************************************************************************
289 * initialization code                                                             *
290 *****************************************************************************/
291const char *_malloc_options = NULL;
292
293/* parse the options contained in buf. */
294static void malloc_init_options(const char *buf) {
295
296    while (*buf != '\0') {
297        switch (*buf) {
298        case '>': options.page_cache <<= 1;                        break;
299        case '<': options.page_cache >>= 1;                        break;
300        case 'a': options.wfatal = false;                        break;
301        case 'A': options.wfatal = true;                        break;
302        case 'b': options.block_cache--;                        break;
303        case 'B': options.block_cache++;                        break;
304        case 'j': options.fill = false;                                break;
305        case 'J': options.fill = true; options.fbyte = 0xd0;        break;
306        case 'l': options.optimize = false;                     break;
307        case 'L': options.optimize = true;                      break;
308        case 'r': options.force_realloc = false;                break;
309        case 'R': options.force_realloc = true;                        break;
310        case 's': options.sanity = false;                        break;
311        case 'S': options.sanity = true;                        break;
312        case 'v': options.sysv_compat = false;                        break;
313        case 'V': options.sysv_compat = true;                        break;
314        case 'z': options.fill = false;                                break;
315        case 'Z': options.fill = true; options.fbyte = 0x0;        break;
316        }
317        buf++;
318    }
319}
320
321static int recursing;
322
323/* output functions for warnings and such.  keeps us from trapping recursive
324 * calls on accident. */
325static inline void malloc_debug(char *msg, ...) {
326#ifndef NDEBUG
327    va_list vl;
328
329    va_start(vl, msg);
330    recursing--;
331    log_vmsg(LOGTYPE_DEBUG, LOG_MODULENAME, msg, vl);
332    va_end(vl);
333    recursing++;
334#endif
335}
336
337static inline void malloc_error(char *msg, ...) {
338    va_list vl;
339
340    va_start(vl, msg);
341    recursing--;
342    log_vmsg(LOGTYPE_ERROR, LOG_MODULENAME, msg, vl);
343    abort();
344    va_end(vl);
345    recursing++;
346}
347
348static void malloc_warn(char *msg, ...) {
349    va_list vl;
350
351    va_start(vl, msg);
352    recursing--;
353    log_vmsg(LOGTYPE_WARN, LOG_MODULENAME, msg, vl);
354    if (options.wfatal)
355        abort();
356    va_end(vl);
357    recursing++;
358}
359
360/* initiate the malloc subsystem.  get the page size and then initiate each
361 * entry in the heaps array. */
362static bool malloc_initialized = false;
363static void malloc_init(void) {
364    int i;
365    size_t sz;
366#define BUFSIZE 64
367    char buf[BUFSIZE];
368    const char *s;
369
370    options.page_cache = MALLOC_PAGE_CACHE;
371    options.block_cache = MALLOC_CACHE_BLOCKS;
372    options.optimize = true;
373
374#ifdef HAVE_READLINK
375    if ((i = readlink(MALLOC_CONFIG_FILE, buf, BUFSIZE)) >= BUFSIZE)
376        i = BUFSIZE - 1;
377    buf[i] = '\0';
378    malloc_init_options(buf);
379#endif
380#ifdef HAVE_GETENV
381    if ((s = getenv("MALLOC_OPTIONS")) != NULL)
382        malloc_init_options(s);
383#endif
384    if ((s = _malloc_options) != NULL)
385        malloc_init_options(s);
386
387    page_desc.start = page_desc.end = sbrk(0);
388    s = next_page_boundary(page_desc.start);
389    if (brk((const void *)s) != 0)
390        abort();
391    page_desc.start = page_desc.end = (void *)s;
392
393    for (i = 0, sz = MALLOC_MIN_SIZE; i < MALLOC_HEAP_COUNT;
394            i++, sz += MALLOC_FUZZ) {
395        heaps[i].size = sz;
396        heaps[i].bpages = 1;
397    }
398    malloc_initialized = true;
399}
400
401/* these are the support functions.. */
402static struct block *create_block(struct heap *);
403static void destroy_block(struct block *);
404static void *alloc_block(struct block *);
405static void free_block(struct block *, void *);
406static struct block *find_block(void *, size_t *);
407
408/* do a 'sorted' insert into the open blocks list for a heap.  this ensures
409 * that the blocks with the lowest memory addresses are used, which makes the
410 * library more likely to wrangle free towards the end of memory. */
411static void insert_open_block(struct block *blk) {
412    struct block *bp;
413
414    if (LIST_EMPTY(&blk->heap->oblist)) {
415        LIST_INSERT_HEAD(&blk->heap->oblist, blk, olp);
416        return;
417    }
418
419    LIST_FOREACH(bp, &blk->heap->oblist, olp) {
420        if (bp > blk) {
421            LIST_INSERT_BEFORE(bp, blk, olp);
422            return;
423        }
424        else if (LIST_NEXT(bp, olp) == NULL)
425            break;
426    }
427    LIST_INSERT_AFTER(bp, blk, olp);
428}
429
430/* create a block for the given heap and fill it in.  this handles the
431 * incremental growth for the heap as well */
432static struct block *create_block(struct heap *hp) {
433    struct block *bp;
434    struct block_elem *ep;
435    int elems;
436    char *mem;
437
438    bp = alloc_pages(hp->bpages);
439    /* tweak the start of the page allocation to be 'BALLOC' so we know memory
440     * in this range is owned by a block */
441    elems = mem_to_pindex(bp);
442    page_desc.parray[elems] = MALLOC_PAGE_BALLOC;
443    bp->heap = hp;
444    mem = bp->start = (char *)bp + sizeof(struct block);
445    /* there may be some leftover space at the end of the block,
446     * unfortunately.. we calculate the number of actual full elements in this
447     * block using size */
448    elems = ((MALLOC_PAGESIZE * hp->bpages) - sizeof(struct block)) / hp->size;
449    bp->end = bp->start + (hp->size * elems);
450    bp->used = 0;
451
452    SLIST_INIT(&bp->elist);
453    while (mem < bp->end) {
454        ep = (struct block_elem *)mem;
455        ep->magic = MALLOC_MAGIC;
456        SLIST_INSERT_HEAD(&bp->elist, ep, lp);
457
458        mem += hp->size;
459    }
460    LIST_INSERT_HEAD(&bp->heap->blist, bp, lp);
461    insert_open_block(bp);
462
463    /* now do some accounting */
464    bp->heap->etotal += elems;
465    bp->heap->blocks++;
466    bp->heap->bempty++;
467
468    /* see if bpages needs to be grown (but we only do this if optimization is
469     * left on) */
470    if  (hp->bpages < MALLOC_MAX_PAGES && options.optimize) {
471        hp->bpages *= 2;
472        if (((MALLOC_PAGESIZE * hp->bpages) - sizeof(struct block)) /
473                hp->size > MALLOC_MAX_ELEMS)
474            hp->bpages /= 2; /* guess not */
475    }
476
477    return bp;
478}
479
480/* destroy a block.  usually done when the block is empty, or at shutdown time
481 * by the program.  we are careful not to free the memory contained by the
482 * block until after we have freed the block because the block could be
483 * contained in that memory (yikes..) */
484static void destroy_block(struct block *bp) {
485    struct heap *hp = bp->heap;
486
487    LIST_REMOVE(bp, lp);
488    if (bp->used != elems_in_block(bp))
489        LIST_REMOVE(bp, olp);
490    else
491        hp->bfull--;
492    if (bp->used != 0)
493        malloc_debug("destroying non-empty block");
494    else
495        hp->bempty--;
496    free_pages(bp);
497}
498
499static void *alloc_block(struct block *bp) {
500    struct block_elem *ep;
501
502    if (options.sanity &&
503            (bp->used == elems_in_block(bp) || SLIST_EMPTY(&bp->elist))) {
504        malloc_error("alloc_block(): block has no free elements!");
505        return NULL;
506    }
507   
508    bp->heap->allocs++;
509
510    if (bp->used == 0)
511        bp->heap->bempty--;
512    bp->used++;
513
514    ep = SLIST_FIRST(&bp->elist);
515    if (options.sanity && ep->magic != MALLOC_MAGIC)
516        malloc_warn("alloc_block(%d): apparent memory corruption",
517                bp->heap->size);
518    SLIST_REMOVE_HEAD(&bp->elist, lp);
519    if (SLIST_EMPTY(&bp->elist)) {
520        LIST_REMOVE(bp, olp); /* take it off the open list */
521        bp->heap->bfull++;
522    }
523    ep->magic = 0; /* wipe the magic number */
524    return ep;
525}
526
527/* return 'mem' to the specified block.  this does some sanity checking on the
528 * way. */
529static void free_block(struct block *bp, void *mem) {
530    struct block_elem *ep = mem;
531
532    if (ep->magic == MALLOC_MAGIC) {
533        malloc_warn("free_block(%d, %p): freeing already freed memroy?",
534                bp->heap->size, mem);
535        return;
536    }
537    bp->heap->frees++;
538
539    if (options.fill)
540        memset(mem, options.fbyte, bp->heap->size);
541
542    ep->magic = MALLOC_MAGIC;
543    SLIST_INSERT_HEAD(&bp->elist, ep, lp);
544    if (bp->used == elems_in_block(bp)) {
545        bp->heap->bfull--;
546        insert_open_block(bp);
547    }
548    if (--bp->used == 0) {
549        bp->heap->bempty++;
550        if (bp->heap->bempty > MALLOC_CACHE_BLOCKS)
551            destroy_block(bp);
552    }
553}
554
555/* attempt to find the owning block of a memory address.  if it doesn't reside
556 * in a block but is a page or multipage allocation we return NULL but set
557 * 'size' to the size of the allocated pages.  whew. */
558static struct block *find_block(void *mem, size_t *rsize) {
559    struct block *bp;
560    unsigned int idx, eidx;
561
562    *rsize = 0;
563    if ((char *)mem < page_desc.start ||
564            (eidx = idx = mem_to_pindex(mem)) > page_desc.size) {
565        malloc_warn("find_block(%p): pointer is outside of memory range", mem);
566        return NULL;
567    }
568    while (page_desc.parray[idx] == MALLOC_PAGE_CONTINUED)
569        idx--;
570    if (page_desc.parray[idx] == MALLOC_PAGE_ALLOC) {
571        /* if this was a normal allocation we just say it was the pagesize,
572         * this might not be true but only actually matters for filling, and
573         * free_pages will fill the whole allocation for us. */
574        if (options.sanity && pindex_to_mem(idx) != mem)
575            malloc_warn("find_block(%p): multipage allocation starts at %p",
576                    pindex_to_mem(idx));
577        *rsize = MALLOC_PAGESIZE;
578        return NULL;
579    } else if (page_desc.parray[idx] == MALLOC_PAGE_BALLOC)
580        bp = (struct block *)pindex_to_mem(idx);
581    else {
582        malloc_warn("find_page(%p): pointer did not end up in an allocated "
583                "range (actually in %s memory)",
584                (page_desc.parray[idx] == MALLOC_PAGE_FREE ? "free" :
585                 "unknown"));
586        return NULL;
587    }
588
589    /* bp is valid.. */
590    *rsize = bp->heap->size;
591    return bp;
592}
593
594/* these are the four allocator functions.  in general they pass off work to
595 * subsystem functions declared below. */
596void *calloc(size_t count, size_t size) {
597    void *mem;
598
599    if ((mem = malloc(count * size)) != NULL)
600        memset(mem, 0, count * size);
601    return mem;
602}
603
604void free(void *mem) {
605    struct block *bp;
606    size_t size;
607
608    if (recursing) {
609        malloc_warn("free(%p): recursing in allocator", mem);
610        return;
611    }
612    recursing++;
613
614    if (!malloc_initialized) {
615        malloc_warn("free(%p): no memory has been allocated", mem);
616        recursing--;
617        return;
618    }
619    bp = find_block(mem, &size);
620    if (bp == NULL && size == 0) {
621        malloc_warn("free(%p): memory not found", mem);
622        recursing--;
623        return;
624    }
625   
626    if (bp == NULL) {
627    if (options.fill)
628        memset(mem, options.fbyte, size);
629        free_pages(mem);
630    } else
631        free_block(bp, mem);
632
633    recursing--;
634}
635
636void *malloc(size_t size) {
637    struct block *bp;
638    void *mem;
639
640    if (recursing) {
641        malloc_warn("malloc(%d): recursing in allocator", size);
642        return NULL;
643    }
644    recursing++;
645
646    if (!malloc_initialized)
647        malloc_init();
648
649    if (size == 0) {
650        if (options.sysv_compat) {
651            recursing--;
652            return NULL;
653        } else
654            size = MALLOC_MIN_SIZE;
655    }
656
657    if (size > MALLOC_MAX_SIZE) {
658        /* change 'size' to reflect the number of pages we want */
659        if (size % MALLOC_PAGESIZE != 0)
660            size = (size / MALLOC_PAGESIZE) + 1;
661        else
662            size /= MALLOC_PAGESIZE;
663        recursing--;
664        return alloc_pages(size);
665    }
666
667    /* adjust it for fuzz */
668    if (size % MALLOC_FUZZ != 0)
669        size += MALLOC_FUZZ - (size % MALLOC_FUZZ);
670    /* size should never be smaller than MALLOC_MIN_SIZE now.. unless
671     * MALLOC_FUZZ was mangled. :) */
672
673    /* so it's of an appropriate size, now to figure out what heap it comes
674     * from we just need to apply a simple algorithm.. */
675    size -= MALLOC_MIN_SIZE;
676    size /= MALLOC_FUZZ; /* and now 'size' is actually our heap */
677
678    if ((bp = LIST_FIRST(&heaps[size].oblist)) == NULL) {
679        if ((bp = create_block(&heaps[size])) == NULL) {
680            if (options.nomem_fatal)
681                malloc_error("malloc(%d): out of memory", heaps[size].size);
682            recursing--;
683            return NULL;
684        }
685    }
686    mem = alloc_block(bp);
687    if (options.fill)
688        memset(mem, options.fbyte, heaps[size].size);
689
690    recursing--;
691    return mem; /* huzzah! */
692}
693
694/* realloc is fairly complicated (under the hood): we always frree mem, and we
695 * always freshly allocate.  eh.  problem is we have to find the heap mem came
696 * from because we need to know how big it is. */
697void *realloc(void *mem, size_t newsize) {
698    struct block *bp = NULL;
699    size_t size = 0;
700    void *newmem;
701   
702    if (recursing) {
703        malloc_warn("realloc(%p, %d): recursing in allocator", mem, newsize);
704        return NULL;
705    }
706    recursing++;
707
708    if (!malloc_initialized) {
709        if (mem != NULL) {
710            malloc_warn("realloc(%p, %d): called before initial allocation",
711                    mem, newsize);
712            mem = NULL;
713        }
714        malloc_init();
715    }
716    if (mem == NULL) {
717        recursing--;
718        if (newsize == 0)
719            return NULL;
720        return malloc(newsize);
721    }
722
723    bp = find_block(mem, &size);
724    if (bp == NULL && size == 0) {
725        malloc_warn("realloc(%p, %d): could not find memory", mem, newsize);
726        recursing--;
727        return NULL;
728    }
729
730    /* unset recursing so we can manually call malloc.. */
731    recursing--;
732    if (!options.force_realloc && size >= newsize)
733        return mem; /* no need to move any memory ... */
734
735    if ((newmem = malloc(newsize)) == NULL)
736        return NULL; /* oops.. */
737    if (options.fill)
738        memset(newmem, options.fbyte, newsize);
739    /* we can't use 'size' verbatim because it might be bigger than the new
740     * size, of course. */
741    if (size > newsize)
742        size = newsize;
743    memcpy(newmem, mem, size);
744    if (bp != NULL)
745        free_block(bp, mem);
746    else
747        free_pages(mem);
748    return newmem;
749}
750
751#endif
752/* vi:set ts=8 sts=4 sw=4 tw=76 et: */
Note: See TracBrowser for help on using the repository browser.