new malloc.c, derived from new Minix version

This commit is contained in:
ceriel 1989-11-30 14:59:18 +00:00
parent 30208cda9a
commit fec7208f70

View file

@ -1,116 +1,185 @@
/* $Header$ */ /* replace undef by define */
#undef DEBUG /* check assertions */
#undef SLOWDEBUG /* some extra test loops (requires DEBUG) */
#define CLICK_SIZE 4096 #ifdef DEBUG
#if EM_WSIZE == EM_PSIZE #define ASSERT(b) if (!(b)) assert_failed();
typedef unsigned int vir_bytes;
#else #else
typedef long vir_bytes; #define ASSERT(b) /* empty */
#endif #endif
extern bcopy();
#define ALIGN(x, a) (((x) + (a - 1)) & ~(a - 1)) #ifdef EM_WSIZE == EM_PSIZE
#define BUSY 1 #define ptrint int
#define NEXT(p) (* (char **) (p))
#ifdef pdp
#define BUGFIX 64 /* cannot set break in top 64 bytes */
#else #else
#define BUGFIX 0 #define ptrint long
#endif #endif
#define BRKSIZE 4096
#define PTRSIZE sizeof(char *)
#define Align(x,a) (((x) + (a - 1)) & ~(a - 1))
#define NextSlot(p) (* (char **) ((p) - PTRSIZE))
#define NextFree(p) (* (char **) (p))
/*
* A short explanation of the data structure and algorithms.
* An area returned by malloc() is called a slot. Each slot
* contains the number of bytes requested, but preceeded by
* an extra pointer to the next the slot in memory.
* '_bottom' and '_top' point to the first/last slot.
* More memory is asked for using brk() and appended to top.
* The list of free slots is maintained to keep malloc() fast.
* '_empty' points the the first free slot. Free slots are
* linked together by a pointer at the start of the
* user visable part, so just after the next-slot pointer.
* Free slots are merged together by free().
*/
extern char *sbrk(), *brk(); extern char *sbrk(), *brk();
static char *bottom, *top; static char *_bottom, *_top, *_empty;
static grow(len) static grow(len)
unsigned len; unsigned len;
{ {
register char *p; register char *p;
register int click = CLICK_SIZE;
p = sbrk(0); ASSERT(NextSlot(_top) == 0);
len += (char *) ALIGN((vir_bytes) p, sizeof(char *)) - p; p = (char *) Align((ptrint)_top + len, BRKSIZE);
while (click >= 4) { if (p < _top || brk(p) != 0)
unsigned len1 = ALIGN((vir_bytes) p + len + sizeof(char *), click) - (vir_bytes) p; return(0);
char *p1 = p; NextSlot(_top) = p;
if (p + len1 + BUGFIX < p || (p1 = sbrk(len1)) == (char *) -1) { NextSlot(p) = 0;
click >>= 1; free(_top);
continue; _top = p;
} return(1);
p = p1;
if (top + sizeof(char *) != p) {
/* someone else has done an sbrk */
NEXT(top) = (char *) ((vir_bytes) p | BUSY);
} else {
for (p = bottom; NEXT(p) != 0; p = (char *) (* (vir_bytes *) p & ~BUSY))
;
}
top = p + len1 - sizeof(char *);
NEXT(p) = top;
NEXT(top) = 0;
return 1;
}
return 0;
} }
char *malloc(size) char *malloc(size)
unsigned size; unsigned size;
{ {
register char *p, *next, *new; register char *prev, *p, *next, *new;
register unsigned len = ALIGN(size, sizeof(char *)) + sizeof(char *); register unsigned len, ntries;
if ((p = bottom) == 0) { if (size == 0)
p = sbrk(sizeof(char *)); size = PTRSIZE; /* avoid slots less that 2*PTRSIZE */
sbrk((char *) ALIGN((vir_bytes) p, sizeof(char *)) - p); for (ntries = 0; ntries < 2; ntries++) {
p = (char *) ALIGN((vir_bytes) p, sizeof(char *)); len = Align(size, PTRSIZE) + PTRSIZE;
top = bottom = p; if (_bottom == 0) {
NEXT(p) = 0; p = sbrk(2 * PTRSIZE);
p = (char *) Align((ptrint)p, PTRSIZE);
p += PTRSIZE;
_top = _bottom = p;
NextSlot(p) = 0;
} }
while ((next = NEXT(p)) != 0) #ifdef SLOWDEBUG
if ((vir_bytes) next & BUSY) /* already in use */ for (p = _bottom; (next = NextSlot(p)) != 0; p = next)
p = (char *) ((vir_bytes) next & ~BUSY); ASSERT(next > p);
else { ASSERT(p == _top);
while ((new = NEXT(next)) != 0 && !((vir_bytes) new & BUSY)) #endif
next = new; for (prev = 0, p = _empty; p != 0; prev = p, p = NextFree(p)) {
if (next - p >= len) { /* fits */ next = NextSlot(p);
if ((new = p + len) < next) /* too big */ new = p + len;
NEXT(new) = next; if (new > next)
NEXT(p) = (char *) ((vir_bytes) new | BUSY); continue; /* too small */
return(p + sizeof(char *)); if (new + PTRSIZE < next) { /* too big, so split */
/* + PTRSIZE avoids tiny slots on free list */
NextSlot(new) = next;
NextSlot(p) = new;
NextFree(new) = NextFree(p);
NextFree(p) = new;
} }
p = next; if (prev)
NextFree(prev) = NextFree(p);
else
_empty = NextFree(p);
return(p);
} }
return grow(len) ? malloc(size) : 0; if (grow(len) == 0)
break;
}
ASSERT(ntries != 2);
return(0);
} }
char *realloc(old, size) char *realloc(old, size)
char *old; char *old;
unsigned size; unsigned size;
{ {
register char *p = old - sizeof(char *), *next, *new; register char *prev, *p, *next, *new;
register unsigned len = ALIGN(size, sizeof(char *)) + sizeof(char *), n; register unsigned len, n;
next = (char *) (* (vir_bytes *) p & ~BUSY); len = Align(size, PTRSIZE) + PTRSIZE;
n = next - old; /* old size */ next = NextSlot(old);
while ((new = NEXT(next)) != 0 && !((vir_bytes) new & BUSY)) n = (int)(next - old); /* old length */
next = new; /*
if (next - p >= len) { /* does it still fit */ * extend old if there is any free space just behind it
if ((new = p + len) < next) { /* even too big */ */
NEXT(new) = next; for (prev = 0, p = _empty; p != 0; prev = p, p = NextFree(p)) {
NEXT(p) = (char *) ((vir_bytes) new | BUSY); if (p > next)
} break;
if (p == next) { /* 'next' is a free slot: merge */
NextSlot(old) = NextSlot(p);
if (prev)
NextFree(prev) = NextFree(p);
else else
NEXT(p) = (char *) ((vir_bytes) next | BUSY); _empty = NextFree(p);
next = NextSlot(old);
break;
}
}
new = old + len;
/*
* Can we use the old, possibly extended slot?
*/
if (new <= next) { /* it does fit */
if (new + PTRSIZE < next) { /* too big, so split */
/* + PTRSIZE avoids tiny slots on free list */
NextSlot(new) = next;
NextSlot(old) = new;
free(new);
}
return(old); return(old);
} }
if ((new = malloc(size)) == 0) /* it didn't fit */ if ((new = malloc(size)) == 0) /* it didn't fit */
return(0); return(0);
bcopy(old, new, n); /* n < size */ bcopy(old, new, n); /* n < size */
* (vir_bytes *) p &= ~BUSY; free(old);
return(new); return(new);
} }
free(p) free(p)
char *p; char *p;
{ {
* (vir_bytes *) (p - sizeof(char *)) &= ~BUSY; register char *prev, *next;
ASSERT(NextSlot(p) > p);
for (prev = 0, next = _empty; next != 0; prev = next, next = NextFree(next))
if (p < next)
break;
NextFree(p) = next;
if (prev)
NextFree(prev) = p;
else
_empty = p;
if (next) {
ASSERT(NextSlot(p) <= next);
if (NextSlot(p) == next) { /* merge p and next */
NextSlot(p) = NextSlot(next);
NextFree(p) = NextFree(next);
} }
}
if (prev) {
ASSERT(NextSlot(prev) <= p);
if (NextSlot(prev) == p) { /* merge prev and p */
NextSlot(prev) = NextSlot(p);
NextFree(prev) = NextFree(p);
}
}
}
#ifdef DEBUG
static assert_failed()
{
write(2, "assert failed in lib/malloc.c\n", 30);
abort();
}
#endif