/*
* (c) Copyright 1990 Conor P. Cahill (uunet!virtech!cpcahil).
* You may copy, distribute, and use this software as long as this
* copyright statement is not removed.
*/
#include <stdio.h>
#include <fcntl.h>
#include "malloc.h"
#include "tostring.h"
/*
* Function: malloc()
*
* Purpose: memory allocator
*
* Arguments: size - size of data area needed
*
* Returns: pointer to allocated area, or NULL if unable
* to allocate addtional data.
*
* Narrative:
*
*/
#ifndef lint
static
char rcs_hdr[] = "$Id: malloc.c,v 1.6 90/05/11 00:13:09 cpcahil Exp $";
#endif
int malloc_checking;
char * malloc_data_start;
char * malloc_data_end;
struct mlist * malloc_end;
int malloc_errfd = 2;
int malloc_errno;
int malloc_fatal_level = M_HANDLE_CORE;
struct mlist malloc_start;
int malloc_warn_level;
char *
malloc(size)
unsigned int size;
{
char * func = "malloc";
char * getenv();
void malloc_fatal();
void malloc_init();
void malloc_memset();
void malloc_split();
void malloc_warning();
unsigned int need;
struct mlist * oldptr;
struct mlist * ptr;
char * sbrk();
/*
* If this is the first call to malloc...
*/
if( malloc_data_start == (char *) 0 )
{
malloc_init();
}
/*
* If malloc chain checking is on, go do it.
*/
if( malloc_checking )
{
(void) malloc_chain_check(1);
}
/*
* always make sure there is at least on extra byte in the malloc
* area so that we can verify that the user does not overrun the
* data area.
*/
size++;
/*
* Now look for a free area of memory of size bytes...
*/
oldptr = NULL;
for(ptr = &malloc_start; ; ptr = ptr->next)
{
/*
* Since the malloc chain is a forward only chain, any
* pointer that we get should always be positioned in
* memory following the previous pointer. If this is not
* so, we must have a corrupted chain.
*/
if( ptr )
{
if(ptr < oldptr )
{
malloc_errno = M_CODE_CHAIN_BROKE;
malloc_fatal(func);
return(NULL);
}
oldptr = ptr;
}
else if( oldptr != malloc_end )
{
/*
* This should never happen. If it does, then
* we got a real problem.
*/
malloc_errno = M_CODE_NO_END;
malloc_fatal(func);
return(NULL);
}
/*
* if this element is already in use...
*/
if( ptr && ((ptr->flag & M_INUSE) != 0) )
{
continue;
}
/*
* if there isn't room for this block..
*/
if( ptr && (ptr->s.size < size) )
{
continue;
}
/*
* If ptr is null, we have run out of memory and must sbrk more
*/
if( ptr == NULL )
{
need = (size + M_SIZE) * (size > 100*1024 ? 1:2);
if( need < M_BLOCKSIZE )
{
need = M_BLOCKSIZE;
}
else if( need & (M_BLOCKSIZE-1) )
{
need &= ~(M_BLOCKSIZE-1);
need += M_BLOCKSIZE;
}
ptr = (struct mlist *) sbrk((int)need);
if( ptr == (struct mlist *) -1 )
{
malloc_errno = M_CODE_NOMORE_MEM;
malloc_fatal(func);
}
malloc_data_end = sbrk((int)0);
ptr->prev = oldptr;
ptr->next = (struct mlist *) 0;
ptr->s.size = need - M_SIZE;
ptr->flag = M_MAGIC;
oldptr->next = ptr;
malloc_end = ptr;
} /* if( ptr ==... */
/*
* Now ptr points to a memory location that can store
* this data, so lets go to work.
*/
ptr->r_size = size; /* save requested size */
ptr->flag |= M_INUSE;
/*
* split off unneeded data area in this block, if possible...
*/
malloc_split(ptr);
/*
* re-adjust the requested size so that it is what the user
* actually requested...
*/
ptr->r_size--;
/*
* just to make sure that noone is misusing malloced
* memory without initializing it, lets set it to
* all '\01's. We call local_memset() because memset()
* may be checking for malloc'd ptrs and this isn't
* a malloc'd ptr yet.
*/
malloc_memset(ptr->data,M_FILL,(int)ptr->s.size);
return( ptr->data);
} /* for(... */
} /* malloc(... */
/*
* Function: malloc_split()
*
* Purpose: to split a malloc segment if there is enough room at the
* end of the segment that isn't being used
*
* Arguments: ptr - pointer to segment to split
*
* Returns: nothing of any use.
*
* Narrative:
* get the needed size of the module
* round the size up to appropriat boundry
* calculate amount of left over space
* if there is enough left over space
* create new malloc block out of remainder
* if next block is free
* join the two blocks together
* fill new empty block with free space filler
* re-adjust pointers and size of current malloc block
*
*
*
* Mod History:
* 90/01/27 cpcahil Initial revision.
*/
void
malloc_split(ptr)
struct mlist * ptr;
{
extern struct mlist * malloc_end;
void malloc_join();
void malloc_memset();
int rest;
int size;
struct mlist * tptr;
size = ptr->r_size;
/*
* roundup size to the appropriate boundry
*/
M_ROUNDUP(size);
/*
* figure out how much room is left in the array.
* if there is enough room, create a new mlist
* structure there.
*/
if( ptr->s.size > size )
{
rest = ptr->s.size - size;
}
else
{
rest = 0;
}
if( rest > (M_SIZE+M_RND) )
{
tptr = (struct mlist *) (ptr->data+size);
tptr->prev = ptr;
tptr->next = ptr->next;
tptr->flag = M_MAGIC;
tptr->s.size = rest - M_SIZE;
/*
* If possible, join this segment with the next one
*/
malloc_join(tptr, tptr->next,0,0);
if( tptr->next )
{
tptr->next->prev = tptr;
}
malloc_memset(tptr->data,M_FREE_FILL, (int)tptr->s.size);
ptr->next = tptr;
ptr->s.size = size;
if( malloc_end == ptr )
{
malloc_end = tptr;
}
}
} /* malloc_split(... */
/*
* Function: malloc_join()
*
* Purpose: to join two malloc segments together (if possible)
*
* Arguments: ptr - pointer to segment to join to.
* nextptr - pointer to next segment to join to ptr.
*
* Returns: nothing of any values.
*
* Narrative:
*
* Mod History:
* 90/01/27 cpcahil Initial revision.
*/
void
malloc_join(ptr,nextptr, inuse_override, fill_flag)
struct mlist * ptr;
struct mlist * nextptr;
int inuse_override;
int fill_flag;
{
unsigned int newsize;
if( ptr && ! (inuse_override || (ptr->flag & M_INUSE)) &&
nextptr && ! (nextptr->flag & M_INUSE) &&
((ptr->data+ptr->s.size) == (char *) nextptr) )
{
if( malloc_end == nextptr )
{
malloc_end = ptr;
}
ptr->next = nextptr->next;
newsize = nextptr->s.size + M_SIZE;
/*
* if we are to fill and this segment is in use,
* fill in with M_FILL newly added space...
*/
if(fill_flag && (ptr->flag & M_INUSE) )
{
malloc_memset(ptr->data+ptr->s.size,
M_FILL, (int)(nextptr->s.size + M_SIZE));
}
ptr->s.size += newsize;
if( ptr->next )
{
ptr->next->prev = ptr;
}
}
} /* malloc_join(... */
/*
* The following mess is just to ensure that the versions of these functions in
* the current library are included (to make sure that we don't accidentaly get
* the libc versions. (This is the lazy man's -u ld directive)
*/
void free();
int strcmp();
int memcmp();
char * realloc();
void (*malloc_void_funcs[])() =
{
free,
};
int (*malloc_int_funcs[])() =
{
strcmp,
memcmp,
};
char * (*malloc_char_star_funcs[])() =
{
realloc,
};
/*
* This is malloc's own memset which is used without checking the parameters.
*/
void
malloc_memset(ptr,byte,len)
char * ptr;
char byte;
int len;
{
while(len-- > 0)
{
*ptr++ = byte;
}
} /* malloc_memset(... */
/*
* Function: malloc_fatal()
*
* Purpose: to display fatal error message and take approrpriate action
*
* Arguments: funcname - name of function calling this routine
*
* Returns: nothing of any value
*
* Narrative:
*
* Notes: This routine does not make use of any libc functions to build
* and/or disply the error message. This is due to the fact that
* we are probably at a point where malloc is having a real problem
* and we don't want to call any function tha