diff options
author | archie <archie@FreeBSD.org> | 2002-03-13 01:42:33 +0000 |
---|---|---|
committer | archie <archie@FreeBSD.org> | 2002-03-13 01:42:33 +0000 |
commit | 4ff830618672fdf2a2864c71864aea5adc8d03e8 (patch) | |
tree | 0df1490a2b1aa94853b37fed5acf74bb6a508a3e /sys | |
parent | 7888fb830149b836a2f4563f392442e88ef59376 (diff) | |
download | FreeBSD-src-4ff830618672fdf2a2864c71864aea5adc8d03e8.zip FreeBSD-src-4ff830618672fdf2a2864c71864aea5adc8d03e8.tar.gz |
Add realloc() and reallocf(), and make free(NULL, ...) acceptable.
Reviewed by: alfred
Diffstat (limited to 'sys')
-rw-r--r-- | sys/kern/kern_malloc.c | 74 | ||||
-rw-r--r-- | sys/sys/malloc.h | 4 |
2 files changed, 78 insertions, 0 deletions
diff --git a/sys/kern/kern_malloc.c b/sys/kern/kern_malloc.c index 0446b1f..a8eec9c 100644 --- a/sys/kern/kern_malloc.c +++ b/sys/kern/kern_malloc.c @@ -57,6 +57,16 @@ #include <machine/cpu.h> #endif +/* + * When realloc() is called, if the new size is sufficiently smaller than + * the old size, realloc() will allocate a new, smaller block to avoid + * wasting memory. 'Sufficiently smaller' is defined as: newsize <= + * oldsize / 2^n, where REALLOC_FRACTION defines the value of 'n'. + */ +#ifndef REALLOC_FRACTION +#define REALLOC_FRACTION 1 /* new block if <= half the size */ +#endif + MALLOC_DEFINE(M_CACHE, "cache", "Various Dynamically allocated caches"); MALLOC_DEFINE(M_DEVBUF, "devbuf", "device driver memory"); MALLOC_DEFINE(M_TEMP, "temp", "misc temporary data buffers"); @@ -294,6 +304,10 @@ free(addr, type) #endif register struct malloc_type *ksp = type; + /* free(NULL, ...) does nothing */ + if (addr == NULL) + return; + KASSERT(kmembase <= (char *)addr && (char *)addr < kmemlimit, ("free: address %p out of range", (void *)addr)); kup = btokup(addr); @@ -400,6 +414,66 @@ free(addr, type) } /* + * realloc: change the size of a memory block + */ +void * +realloc(addr, size, type, flags) + void *addr; + unsigned long size; + struct malloc_type *type; + int flags; +{ + struct kmemusage *kup; + unsigned long alloc; + void *newaddr; + + /* realloc(NULL, ...) is equivalent to malloc(...) */ + if (addr == NULL) + return (malloc(size, type, flags)); + + /* Sanity check */ + KASSERT(kmembase <= (char *)addr && (char *)addr < kmemlimit, + ("realloc: address %p out of range", (void *)addr)); + + /* Get the size of the original block */ + kup = btokup(addr); + alloc = 1 << kup->ku_indx; + if (alloc > MAXALLOCSAVE) + alloc = kup->ku_pagecnt << PAGE_SHIFT; + + /* Reuse the original block if appropriate */ + if (size <= alloc + && (size > (alloc >> REALLOC_FRACTION) || alloc == MINALLOCSIZE)) + return (addr); + + /* Allocate a new, bigger (or smaller) block */ + if ((newaddr = malloc(size, type, flags)) == NULL) + return (NULL); + + /* Copy over original contents */ + bcopy(addr, newaddr, min(size, alloc)); + free(addr, type); + return (newaddr); +} + +/* + * reallocf: same as realloc() but free memory on failure. + */ +void * +reallocf(addr, size, type, flags) + void *addr; + unsigned long size; + struct malloc_type *type; + int flags; +{ + void *mem; + + if ((mem = realloc(addr, size, type, flags)) == NULL) + free(addr, type); + return (mem); +} + +/* * Initialize the kernel memory allocator */ /* ARGSUSED*/ diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h index ce99327..5f3ec3d 100644 --- a/sys/sys/malloc.h +++ b/sys/sys/malloc.h @@ -173,6 +173,10 @@ void free __P((void *addr, struct malloc_type *type)); void *malloc __P((unsigned long size, struct malloc_type *type, int flags)); void malloc_init __P((void *)); void malloc_uninit __P((void *)); +void *realloc __P((void *addr, unsigned long size, + struct malloc_type *type, int flags)); +void *reallocf __P((void *addr, unsigned long size, + struct malloc_type *type, int flags)); #endif /* _KERNEL */ #endif /* !_SYS_MALLOC_H_ */ |