/* vim: set filetype=c:
 *
 * malloc.c86  -  Memory allocation
 *
 * This file is based on malloc source code written by Doug Lea, who
 * has released it into the public domain. Only some concepts, various
 * C code and macros and a couple of comments of Doug's source code
 * have been included here. This malloc implementation is space-con-
 * serving but quite slow. It is sufficient for the i386 runtime
 * library. For a more complete and much faster implementation take
 * a look at Doug's sources.
 *
 *
 * Copyright (C) 2004-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: malloc.c86,v 1.4 2007/01/06 18:30:51 gkminix Exp $
 */


/* Include target system C headers */
#include <common.h86>
#include <i386/memory.h86>
#include <i386/string.h86>



/*
 ************************************************************************
 *
 * Following is needed on implementations whereby long > size_t.
 * The problem is caused because the code performs subtractions of
 * size_t values and stores the result in long values.  In the case
 * where long > size_t and the first value is actually less than
 * the second value, the resultant value is positive.  For example,
 * (long)(x - y) where x = 0 and y is 1 ends up being 0x00000000FFFFFFFF
 * which is 2*31 - 1 instead of 0xFFFFFFFFFFFFFFFF.  This is due to the
 * fact that assignment from unsigned to signed won't sign extend.
 */
#define long_sub_size_t(x, y) ( (x < y) ? -((long)(y - x)) : (x - y) );




/*
 ************************************************************************
 *
 *  malloc_chunk details:
 *
 *   (The following includes lightly edited explanations by Colin Plumb.)
 *
 *   Chunks of memory are maintained using a `boundary tag' method as
 *   described in e.g., Knuth or Standish.  (See the paper by Paul
 *   Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a
 *   survey of such techniques.)  Sizes of free chunks are stored both
 *   in the front of each chunk and at the end.  This makes
 *   consolidating fragmented chunks into bigger chunks very fast.  The
 *   size fields also hold bits representing whether chunks are free or
 *   in use.
 *
 *   An allocated chunk looks like this:  
 *
 *
 *   chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *           |             Size of previous chunk, if allocated            | |
 *           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *           |             Size of chunk, in bytes                         |P|
 *     mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *           |             User data starts here...                          .
 *           .                                                               .
 *           .             (malloc_usable_space() bytes)                     .
 *           .                                                               |
 *    next-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *           |             Size of chunk                                     |
 *           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 *
 *   Where "chunk" is the front of the chunk for the purpose of most of
 *   the malloc code, but "mem" is the pointer that is returned to the
 *   user.  "Next" is the beginning of the next contiguous chunk.
 *
 *   Chunks always begin on even word boundries, so the mem portion
 *   (which is returned to the user) is also on an even word boundary, and
 *   thus double-word aligned.
 *
 *   Free chunks are stored in circular doubly-linked lists, and look like this:
 *
 *   chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *           |             Size of previous chunk                            |
 *           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *   `head:' |             Size of chunk, in bytes                         |P|
 *     mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *           |             Forward pointer to next chunk in list             |
 *           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *           |             Back pointer to previous chunk in list            |
 *           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *           |             Unused space (may be 0 bytes long)                .
 *           .                                                               .
 *           .                                                               |
 *    next-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *   `foot:' |             Size of chunk, in bytes                           |
 *           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 *   The P (PREV_INUSE) bit, stored in the unused low-order bit of the
 *   chunk size (which is always a multiple of two words), is an in-use
 *   bit for the *previous* chunk.  If that bit is *clear*, then the
 *   word before the current chunk size contains the previous chunk
 *   size, and can be used to find the front of the previous chunk.
 *   (The very first chunk allocated always has this bit set,
 *   preventing access to non-existent (or non-owned) memory.)
 *
 *   Note that the `foot' of the current chunk is actually represented
 *   as the prev_size of the NEXT chunk. (This makes it easier to
 *   deal with alignments etc).
 *
 *   The exception to all this is the special chunk `top', which doesn't
 *   bother using the trailing size field since there is no next conti-
 *   guous chunk that would have to index off it. (After initialization,
 *   `top' is forced to always exist.  If it would become less than
 *   MINSIZE bytes long, it is replenished via malloc_extend_top.)
 *
 *   Available chunks are kept in any of several places (all declared below):
 *
 *   * `binlist': This list contains all consolidated free chunks. It
 *      is doubly linked. All procedures maintain the invariant that no
 *      consolidated chunk physically borders another one. Chunks in this
 *      bin are kept in size order, with ties going to the approximately
 *      least recently used chunk.
 *
 *   * `top': The top-most available chunk (i.e., the one bordering the
 *      end of available memory) is treated specially. It is used only
 *      if no other chunk is available.
 *
 *   * `last_remainder': A bin holding only the remainder of the
 *      most recently split (non-top) chunk. This bin is checked
 *      before other non-fitting chunks, so as to provide better
 *      locality for runs of sequentially allocated chunks. 
 */


/* Chunk info structure */
struct malloc_chunk
{
  size_t  prev_size;		/* Size of previous chunk (if free) */
  size_t  size;			/* Size in bytes, including overhead */
  __nptr  fd;			/* double links -- used only if free */
  __nptr  bk;
};

typedef struct malloc_chunk *mchunkptr;


/* Field-extraction macros */
#define forward(b)   (mchunkptr)((int)((b)->fd))
#define backward(b)  (mchunkptr)((int)((b)->bk))


/*  Sizes and alignments */
#define SIZE_SZ                (sizeof(size_t))
#define MALLOC_ALIGNMENT       (SIZE_SZ + SIZE_SZ)
#define MALLOC_ALIGN_MASK      (MALLOC_ALIGNMENT - 1)
#define MINSIZE                (sizeof(struct malloc_chunk))
#define PAGE_SIZE		1024


/* Conversion from malloc headers to user pointers, and back */
#define chunk2mem(p)   ((void *)((char *)(p) + 2 * SIZE_SZ))
#define mem2chunk(mem) ((mchunkptr)((char *)(mem) - 2 * SIZE_SZ))


/* Pad request bytes into a usable size */
#define request2size(req) \
 (((long)((req) + SIZE_SZ) < (long)(MINSIZE)) ? \
	((MINSIZE + MALLOC_ALIGN_MASK) & ~(MALLOC_ALIGN_MASK)) : \
	(((req) + (SIZE_SZ + MALLOC_ALIGN_MASK)) & ~(MALLOC_ALIGN_MASK)))




/* 
 ************************************************************************
 *
 *		Physical chunk operations  
 *
 ************************************************************************
 */


/*
 * Dealing with chunk pointers
 */

/* Size field is or'ed with PREV_INUSE when previous adjacent chunk in use */
#define PREV_INUSE 0x1 


/* Ptr to next physical malloc_chunk. */
#define next_chunk(p) ((mchunkptr)(((char *)(p)) + ((p)->size & ~PREV_INUSE)))


/* Ptr to previous physical malloc_chunk */
#define prev_chunk(p) ((mchunkptr)(((char *)(p)) - ((p)->prev_size)))


/* Treat space at ptr + offset as a chunk */
#define chunk_at_offset(p, s) ((mchunkptr)(((char *)(p)) + (s)))



/* 
 * Dealing with use bits 
 */

/* Extract p's inuse bit */
#define inuse(p) ((next_chunk(p)->size) & PREV_INUSE)


/* Extract inuse bit of previous chunk */
#define prev_inuse(p) ((p)->size & PREV_INUSE)


/* Check/set/clear inuse bits in known places */
#define inuse_bit_at_offset(p, s)     (chunk_at_offset(p, s)->size & PREV_INUSE)
#define set_inuse_bit_at_offset(p, s) (chunk_at_offset(p, s)->size |= PREV_INUSE)




/* 
 * Dealing with size fields 
 */

/* Get size, ignoring use bits */
#define chunksize(p)         ((p)->size & ~(PREV_INUSE))


/* Set size at head, without disturbing its use bit */
#define set_head_size(p, s)  ((p)->size = (((p)->size & PREV_INUSE) | (s)))


/* Set size/use ignoring previous bits in header */
#define set_head(p, s)       ((p)->size = (s))


/* Set size at footer (only when chunk is not in use) */
#define set_foot(p, s)       (chunk_at_offset(p, s)->prev_size = (s))




/*
 ************************************************************************
 *
 *	Bin operations
 *
 ************************************************************************
 */


/* The topmost chunk */
static mchunkptr top = NULL;


/* Remainder from last split */
static mchunkptr last_remainder = NULL;


/* Bin holding all consolidated free chunks */
static mchunkptr binlist = NULL;
static mchunkptr binend = NULL;


/* The first value returned from sbrk */
static void *sbrk_base = (void *)(-1);



/* 
 * Place chunk p of size s in its bin, in size order,
 * putting it ahead of others of same size.
 */
static void frontlink(mchunkptr ptr, size_t size)
{
  register mchunkptr cp1, cp2;

  cp1 = binlist;
  cp2 = NULL;
  while (cp1 != NULL && size < chunksize(cp1)) {
	cp2 = cp1;
	cp1 = forward(cp1);
  }
  ptr->fd = _NP(cp1);
  ptr->bk = _NP(cp2);
  if (cp1 != NULL)
	cp1->bk = _NP(ptr);
  else
	binend = ptr;
  if (cp2 != NULL)
	cp2->fd = _NP(ptr);
  else
	binlist = ptr;
}



/*
 * Take a chunk off a list
 */
static void unlink(mchunkptr ptr)
{
  register mchunkptr cp1, cp2;

  cp1 = backward(ptr);
  cp2 = forward(ptr);
  if (cp1 == NULL)
	binlist = cp2;
  else
	cp1->fd = _NP(cp2);
  if (cp2 == NULL)
	binend = cp1;
  else
	cp2->bk = _NP(cp1);
}



/*
 * Place ptr as the last remainder
 */
static inline void link_last_remainder(mchunkptr ptr)
{
  last_remainder = ptr;
  ptr->fd = ptr->bk = NNULL;
}



/* 
 * Extend the top-most chunk by obtaining memory from system.
 * Main interface to sbrk.
 */
static void malloc_extend_top(size_t nb)
{
  void     *brk;		/* return value from sbrk */
  void     *new_brk;		/* return of 2nd sbrk call */
  size_t    front_misalign;	/* unusable bytes at front of sbrked space */
  size_t    correction;		/* bytes for 2nd sbrk call */
  size_t    top_size;		/* new size of top chunk */
  mchunkptr old_top;
  size_t    old_top_size = 0;
  void     *old_end      = NULL;
  size_t    sbrk_size    = nb + MINSIZE;

  /* Determine size and end pointer of old top chunk */
  if ((old_top = top) != NULL) {
	old_top_size = chunksize(old_top);
	old_end = (void *)(chunk_at_offset(old_top, old_top_size));
  }

  /*
   * If not the first time through, round to preserve page boundary.
   * Otherwise, we need to correct to a page size below anyway.
   * (We also correct below if an intervening foreign sbrk call.)
   */
  if (sbrk_base != (void *)(-1))
	sbrk_size = (sbrk_size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);

  brk = sbrk(sbrk_size);

  /* Fail if sbrk failed or if a foreign sbrk call killed our space */
  if (brk == (void *)(-1) || (old_end != NULL && brk < old_end))
	return;     

  /* Insert the newly allocated system memory into bin list */
  if (brk == old_end) {
	/* Can just add bytes to current top */
	top_size = sbrk_size + old_top_size;
	set_head(top, top_size | PREV_INUSE);
  } else {
	/* First time through. Record base */
	if (sbrk_base == (void *)(-1))
		sbrk_base = brk;

	/* Guarantee alignment of first new chunk made from this space */
	front_misalign = (unsigned long)chunk2mem(brk) & MALLOC_ALIGN_MASK;
	correction = (front_misalign > 0 ?
				(MALLOC_ALIGNMENT) - front_misalign : 0);
	brk += correction;

	/* Guarantee the next brk will be at a page boundary */
	correction += PAGE_SIZE -
			((unsigned long)(brk + sbrk_size) & (PAGE_SIZE - 1));

	/* Allocate correction */
	new_brk = (void *)(sbrk(correction));
	if (new_brk == (void *)(-1))
		return; 

	/* Add new memory to current top */
	top = (mchunkptr)brk;
	top_size = new_brk - brk + correction;
	set_head(top, top_size | PREV_INUSE);

	/*
	 * There must have been an intervening foreign sbrk call. A double
	 * fencepost is necessary to prevent consolidation.
	 */
	if (old_top != NULL) {
		/*
		 * If not enough space to do this, then user did something
		 * very wrong.
		 */
		if (old_top_size < MINSIZE) {
			set_head(top, PREV_INUSE);
			return;
		}

		/* Also keep size a multiple of MALLOC_ALIGNMENT */
		old_top_size = (old_top_size - 3*SIZE_SZ) & ~MALLOC_ALIGN_MASK;
		set_head_size(old_top, old_top_size);
		chunk_at_offset(old_top, old_top_size          )->size =
				SIZE_SZ|PREV_INUSE;
		chunk_at_offset(old_top, old_top_size + SIZE_SZ)->size =
				SIZE_SZ|PREV_INUSE;

		/* If possible, release the rest. */
		if (old_top_size >= MINSIZE) 
			free(chunk2mem(old_top));
	}
  }
}




/*
 ************************************************************************
 *
 *		Main public routines
 *
 ************************************************************************
 */


/*
 * Malloc() Algorithm:
 *
 *   The requested size is first converted into a usable form, `nb'.
 *   This currently means to add 4 bytes overhead plus possibly more to
 *   obtain 8-byte alignment and/or to obtain a size of at least
 *   MINSIZE (currently 8 bytes), the smallest allocatable size.
 *   (All fits are considered `exact' if they are within MINSIZE bytes.)
 *
 *   From there, the first successful of the following steps is taken:
 *
 *     1. The bin of free chunks is scanned, and if a chunk of exactly
 *        the right size is found, it is taken.
 *
 *     2. The most recently remaindered chunk is used if it is big
 *        enough.  This is a form of (roving) first fit, used only in
 *        the absence of exact fits. Runs of consecutive requests use
 *        the remainder of the chunk used for the previous such request
 *        whenever possible. This limited use of a first-fit style
 *        allocation strategy tends to give contiguous chunks
 *        coextensive lifetimes, which improves locality and can reduce
 *        fragmentation in the long run.
 *
 *     3. The bin of free chunks is scanned in increasing size order,
 *        using a chunk big enough to fulfill the request, and splitting
 *        off any remainder.  This search is strictly by best-fit; i.e.,
 *        the smallest (with ties going to approximately the least
 *        recently used) chunk that fits is selected.
 *
 *     4. If large enough, the chunk bordering the end of memory
 *        (`top') is split off. (This use of `top' is in accord with
 *        the best-fit search rule.  In effect, `top' is treated as
 *        larger (and thus less well fitting) than any other available
 *        chunk since it can be extended to be as large as necessary
 *        (up to system limitations).
 *
 *     6. Otherwise, the top of memory is extended by
 *        obtaining more space from the system.
 *        Memory is gathered from the system (in system page-sized
 *        units) in a way that allows chunks obtained across different
 *        sbrk calls to be consolidated, but does not require
 *        contiguous memory. Thus, it should be safe to intersperse
 *        mallocs with other sbrk calls.
 *
 *     All allocations are made from the the `lowest' part of any found
 *     chunk. (The implementation invariant is that prev_inuse is
 *     always true of any allocated chunk; i.e., that each allocated
 *     chunk borders either a previously allocated and still in-use chunk,
 *     or the base of its memory arena.)
 */
void *malloc(size_t bytes)
{
  mchunkptr victim;			/* inspected/selected chunk */
  size_t    victim_size;		/* its size */
  mchunkptr remainder;			/* remainder from a split */
  long      remainder_size;		/* its size */
  size_t    top_size;			/* size of top chunk */
  size_t    nb = request2size(bytes);	/* padded request size; */

  /* Check for exact match */
  for (victim = binend; victim != NULL; victim = backward(victim)) {
	victim_size = chunksize(victim);
	remainder_size = long_sub_size_t(victim_size, nb);
	if (remainder_size >= 0) {
		/* Better try last remainder */
		if (remainder_size >= (long)MINSIZE)
			break;

		/* Exact fit */
		unlink(victim);
		set_inuse_bit_at_offset(victim, victim_size);
		return(chunk2mem(victim));
	}
  }

  /* Try to use the last split-off remainder */
  if ((victim = last_remainder) != NULL) {
	victim_size = chunksize(victim);
	remainder_size = long_sub_size_t(victim_size, nb);
	if (remainder_size >= (long)MINSIZE) {
		/* Re-split */
		remainder = chunk_at_offset(victim, nb);
		set_head(victim, nb | PREV_INUSE);
		link_last_remainder(remainder);
		set_head(remainder, remainder_size | PREV_INUSE);
		set_foot(remainder, remainder_size);
		return(chunk2mem(victim));
	}

	last_remainder = NULL;
	if (remainder_size >= 0) {
		/* Exact fit */
		set_inuse_bit_at_offset(victim, victim_size);
		return(chunk2mem(victim));
	}

	/* Else place in free bin */
	frontlink(victim, victim_size);
  }

  /* Find and use first big enough chunk ... */
  for (victim = binend; victim != NULL; victim = backward(victim)) {
	victim_size = chunksize(victim);
	remainder_size = long_sub_size_t(victim_size, nb);
	if (remainder_size >= (long)MINSIZE) {
		/* Split */
		remainder = chunk_at_offset(victim, nb);
		set_head(victim, nb | PREV_INUSE);
		unlink(victim);
		link_last_remainder(remainder);
		set_head(remainder, remainder_size | PREV_INUSE);
		set_foot(remainder, remainder_size);
		return(chunk2mem(victim));
	} else if (remainder_size >= 0) {
		/* Take */
		set_inuse_bit_at_offset(victim, victim_size);
		unlink(victim);
		return(chunk2mem(victim));
	}
  }

  /* Try to use top chunk */
  if (top == NULL) {
	malloc_extend_top(nb);
	if (top == NULL)
		return(NULL);
  }
  top_size = chunksize(top);
  remainder_size = long_sub_size_t(top_size, nb);
  if (top_size < nb || remainder_size < (long)MINSIZE) {
	malloc_extend_top(nb);
	top_size = chunksize(top);
	remainder_size = long_sub_size_t(top_size, nb);
	if (top_size < nb || remainder_size < (long)MINSIZE)
	      return(NULL);
  }
  victim = top;
  set_head(victim, nb | PREV_INUSE);
  top = chunk_at_offset(victim, nb);
  set_head(top, remainder_size | PREV_INUSE);
  return(chunk2mem(victim));
}



/*
 * Free() Algorithm :
 *
 *   cases:
 *
 *      1. free(0) has no effect.  
 *
 *      2. Other chunks are consolidated as they arrive, and
 *         placed in corresponding bins. (This includes the case of
 *         consolidating with the current `last_remainder').
 */
void free(void *mem)
{
  mchunkptr p;			/* chunk corresponding to mem */
  size_t    hd;			/* its head field */
  size_t    sz;			/* its size */
  mchunkptr next;		/* next contiguous chunk */
  size_t    nextsz;		/* its size */
  size_t    prevsz;		/* size of previous contiguous chunk */
  bool_t    islr;		/* track whether merging with last_remainder */

  /* free(NULL) has no effect */
  if (mem == NULL)
	return;

  /* We always assume having been passed a valid pointer */
  p = mem2chunk(mem);
  hd = p->size;
  sz = hd & ~PREV_INUSE;
  next = chunk_at_offset(p, sz);
  nextsz = chunksize(next);
  
  /* Check if we have to merge with top */
  if (next == top) {
	sz += nextsz;
	if (!(hd & PREV_INUSE)) {
		/* Consolidate backward */
		prevsz = p->prev_size;
		p = chunk_at_offset(p, -prevsz);
		sz += prevsz;
		unlink(p);
	}
	set_head(p, sz | PREV_INUSE);
	top = p;
	return;
  }


  /* Clear inuse bit */
  set_head(next, nextsz);

  /* Consolidate backward */
  islr = FALSE;
  if (!(hd & PREV_INUSE)) {
	prevsz = p->prev_size;
	p = chunk_at_offset(p, -prevsz);
	sz += prevsz;
	if (p->fd == _NP(last_remainder))	/* keep as last_remainder */
		islr = TRUE;
	else
		unlink(p);
  }
  
  /* Consolidate forward */
  if (!(inuse_bit_at_offset(next, nextsz))) {
	sz += nextsz;
	if (!islr && next->fd == _NP(last_remainder)) {
		/* re-insert last_remainder */
		islr = TRUE;
		link_last_remainder(p);   
	} else
		unlink(next);
  }

  /* Link into bin list */
  set_head(p, sz | PREV_INUSE);
  set_foot(p, sz);
  if (!islr)
	frontlink(p, sz);  
}




/*
 * Realloc() Algorithm:
 *
 *   If the reallocation is for additional space, and the chunk can be
 *   extended, it is, else a malloc-copy-free sequence is taken.
 *   There are several different ways that a chunk could be extended.
 *   All are tried:
 *
 *      * Extending forward into following adjacent free chunk.
 *      * Shifting backwards, joining preceding adjacent space
 *      * Both shifting backwards and extending forward.
 *      * Extending into newly sbrked space
 *
 *   realloc with a
 *   size argument of zero (re)allocates a minimum-sized chunk.
 *
 *   If the reallocation is for less space, and the new request is for
 *   a `small' (<512 bytes) size, then the newly unused space is lopped
 *   off and freed.
 */
void *realloc(void *oldmem, size_t bytes)
{
  size_t    nb;			/* padded request size */

  mchunkptr oldp;		/* chunk corresponding to oldmem */
  size_t    oldsize;		/* its size */

  mchunkptr newp;		/* chunk to return */
  size_t    newsize;		/* its size */
  void     *newmem;		/* corresponding user mem */

  mchunkptr next;		/* next contiguous chunk after oldp */
  size_t    nextsize;		/* its size */

  mchunkptr prev;		/* previous contiguous chunk before oldp */
  size_t    prevsize;		/* its size */

  mchunkptr remainder;		/* holds split off extra space from newp */
  size_t    remainder_size;	/* its size */


  /* realloc of null is supposed to be same as malloc */
  if (oldmem == NULL)
	return(malloc(bytes));

  /* Determine chunk pointers and sizes for old memory */
  newp    = oldp    = mem2chunk(oldmem);
  newsize = oldsize = chunksize(oldp);

  /* Pad request size */
  nb = request2size(bytes);

  /* Try expanding forward */
  if ((long)(oldsize) < (long)(nb))  {
	next = chunk_at_offset(oldp, oldsize);
	if (next == top || !inuse(next)) {
		nextsize = chunksize(next);

		/* Forward into top only if a remainder */
		if (next == top) {
			if ((long)(nextsize + newsize) >= (long)(nb + MINSIZE)) {
				newsize += nextsize;
				top = chunk_at_offset(oldp, nb);
				set_head(top, (newsize - nb) | PREV_INUSE);
				set_head_size(oldp, nb);
				return(chunk2mem(oldp));
			}
		}

		/* Forward into next chunk */
		else if (((long)(nextsize + newsize) >= (long)(nb))) { 
			unlink(next);
			newsize  += nextsize;
			goto split;
		}
	  } else {
		next = 0;
		nextsize = 0;
	  }

	  /* Try shifting backwards. */
	  if (!prev_inuse(oldp)) {
		prev = prev_chunk(oldp);
		prevsize = chunksize(prev);
	
		/* try forward + backward first to save a later consolidation */
		if (next != NULL) {
			/* into top */
			if (next == top) {
				if ((long)(nextsize + prevsize + newsize) >=
							(long)(nb + MINSIZE)) {
					unlink(prev);
					newp = prev;
					newsize += prevsize + nextsize;
					newmem = chunk2mem(newp);
					memcpy(newmem, oldmem, oldsize - SIZE_SZ);
					top = chunk_at_offset(newp, nb);
					set_head(top, (newsize - nb) | PREV_INUSE);
					set_head_size(newp, nb);
					return(newmem);
				}
			}

			/* into next chunk */
			else if (((long)(nextsize + prevsize + newsize) >= (long)(nb))) {
				unlink(next);
				unlink(prev);
				newp = prev;
				newsize += nextsize + prevsize;
				newmem = chunk2mem(newp);
				memcpy(newmem, oldmem, oldsize - SIZE_SZ);
				goto split;
			}
		}

		/* backward only */
		if (prev != 0 && (long)(prevsize + newsize) >= (long)nb)  {
			unlink(prev);
			newp = prev;
			newsize += prevsize;
			newmem = chunk2mem(newp);
			memcpy(newmem, oldmem, oldsize - SIZE_SZ);
			goto split;
		}
	}

	/* Must allocate */
	newmem = malloc (bytes);
	if (newmem == NULL)
		return(NULL);

	/*
	 * Avoid copy if newp is next chunk after oldp. (This can only
	 * happen when new chunk is sbrk'ed.)
	 */
	if ( (newp = mem2chunk(newmem)) == next_chunk(oldp)) {
		newsize += chunksize(newp);
		newp = oldp;
		goto split;
	}

	/* Otherwise copy, free, and exit */
	memcpy(newmem, oldmem, oldsize - SIZE_SZ);
	free(oldmem);
	return newmem;
  }

split:  /* split off extra room in old or expanded chunk */

  remainder_size = long_sub_size_t(newsize, nb);
  if (remainder_size >= (long)MINSIZE) {
	remainder = chunk_at_offset(newp, nb);
	set_head_size(newp, nb);
	set_head(remainder, remainder_size | PREV_INUSE);
	set_inuse_bit_at_offset(remainder, remainder_size);
	free(chunk2mem(remainder)); /* let free() deal with it */
  } else {
	set_head_size(newp, newsize);
	set_inuse_bit_at_offset(newp, newsize);
  }
  return(chunk2mem(newp));
}



/*
 * calloc calls malloc, then zeroes out the allocated chunk.
 */
void *calloc(size_t n, size_t elem_size)
{
  mchunkptr p;
  size_t csz;
  void *mem;

  if ((mem = malloc(n * elem_size)) != NULL) {
	p = mem2chunk(mem);
	csz = chunksize(p);
	memset(mem, 0, csz - SIZE_SZ);
  }
  return(mem);
}

