189 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* replace undef by define */
 | |
| #undef	 DEBUG		/* check assertions */
 | |
| #undef	 SLOWDEBUG	/* some extra test loops (requires DEBUG) */
 | |
| 
 | |
| #ifdef DEBUG
 | |
| #define	ASSERT(b)	if (!(b)) assert_failed();
 | |
| #else
 | |
| #define	ASSERT(b)	/* empty */
 | |
| #endif
 | |
| 
 | |
| #ifdef EM_WSIZE == EM_PSIZE
 | |
| #define	ptrint		int
 | |
| #else
 | |
| #define	ptrint		long
 | |
| #endif
 | |
| 
 | |
| #if EM_PSIZE == 2
 | |
| #define BRKSIZE		1024
 | |
| #else
 | |
| #define BRKSIZE		4096
 | |
| #endif
 | |
| #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();
 | |
| static char *_bottom, *_top, *_empty;
 | |
| 
 | |
| static grow(len)
 | |
| unsigned len;
 | |
| {
 | |
|   register char *p;
 | |
| 
 | |
|   ASSERT(NextSlot(_top) == 0);
 | |
|   p = (char *) Align((ptrint)_top + len, BRKSIZE);
 | |
|   if (p < _top || brk(p) != 0)
 | |
| 	return(0);
 | |
|   NextSlot(_top) = p;
 | |
|   NextSlot(p) = 0;
 | |
|   free(_top);
 | |
|   _top = p;
 | |
|   return(1);
 | |
| }
 | |
| 
 | |
| char *malloc(size)
 | |
| unsigned size;
 | |
| {
 | |
|   register char *prev, *p, *next, *new;
 | |
|   register unsigned len, ntries;
 | |
| 
 | |
|   if (size == 0)
 | |
| 	size = PTRSIZE;		/* avoid slots less that 2*PTRSIZE */
 | |
|   for (ntries = 0; ntries < 2; ntries++) {
 | |
| 	len = Align(size, PTRSIZE) + PTRSIZE;
 | |
| 	if (_bottom == 0) {
 | |
| 		p = sbrk(2 * PTRSIZE);
 | |
| 		p = (char *) Align((ptrint)p, PTRSIZE);
 | |
| 		p += PTRSIZE;
 | |
| 		_top = _bottom = p;
 | |
| 		NextSlot(p) = 0;
 | |
| 	}
 | |
| #ifdef SLOWDEBUG
 | |
| 	for (p = _bottom; (next = NextSlot(p)) != 0; p = next)
 | |
| 		ASSERT(next > p);
 | |
| 	ASSERT(p == _top);
 | |
| #endif
 | |
| 	for (prev = 0, p = _empty; p != 0; prev = p, p = NextFree(p)) {
 | |
| 		next = NextSlot(p);
 | |
| 		new = p + len;
 | |
| 		if (new > next)
 | |
| 			continue;		/* too small */
 | |
| 		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;
 | |
| 		}
 | |
| 		if (prev)
 | |
| 			NextFree(prev) = NextFree(p);
 | |
| 		else
 | |
| 			_empty = NextFree(p);
 | |
| 		return(p);
 | |
| 	}
 | |
| 	if (grow(len) == 0)
 | |
| 		break;
 | |
|   }
 | |
|   ASSERT(ntries != 2);
 | |
|   return(0);
 | |
| }
 | |
| 
 | |
| char *realloc(old, size)
 | |
| char *old;
 | |
| unsigned size;
 | |
| {
 | |
|   register char *prev, *p, *next, *new;
 | |
|   register unsigned len, n;
 | |
| 
 | |
|   len = Align(size, PTRSIZE) + PTRSIZE;
 | |
|   next = NextSlot(old);
 | |
|   n = (int)(next - old);			/* old length */
 | |
|   /*
 | |
|    * extend old if there is any free space just behind it
 | |
|    */
 | |
|   for (prev = 0, p = _empty; p != 0; prev = p, p = NextFree(p)) {
 | |
| 	if (p > next)
 | |
| 		break;
 | |
| 	if (p == next) {	/* 'next' is a free slot: merge */
 | |
| 		NextSlot(old) = NextSlot(p);
 | |
| 		if (prev)
 | |
| 			NextFree(prev) = NextFree(p);
 | |
| 		else
 | |
| 			_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);
 | |
|   }
 | |
|   if ((new = malloc(size)) == 0)		/* it didn't fit */
 | |
| 	return(0);
 | |
|   bcopy(old, new, n);				/* n < size */
 | |
|   free(old);
 | |
|   return(new);
 | |
| }
 | |
| 
 | |
| free(p)
 | |
| char *p;
 | |
| {
 | |
|   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
 |