ftp.nice.ch/Attic/openStep/implementation/gnustep/sources/gstep-base-0.2.7.tgz#/gstep-base-0.2.7/src/NSZone.m

This is NSZone.m in view mode; [Download] [Up]

/* Zone memory management.
   Copyright (C) 1995, 1996  Free Software Foundation, Inc.
 
   Author: Mark Lakata <lakata@sseos.lbl.gov>
   Date: January 1995
 
   This file is part of the GNUstep Base Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
   
   This library 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
   Library General Public License for more details.
   
   You should have received a copy of the GNU Library General Public
   License along with this library; if not, write to the Free
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   Description:
 
   These functions manage memory in a way similar to the c library
   functions: malloc() and free().  Instead of allocating small chunks
   of memory with each malloc() call, with this method one must first
   allocate a larger "zone", and then suballocate this in smaller
   chunks.  Many zones can be created, and within each zone, objects
   will be "closer" in virtual memory space thus reducing the need for
   page-swapping.  By intelligently allocating frequently used objects
   from the same zone, you can significantly improve performance on
   systems with paged virtual memory.

   Usage:

   First create a zone with NSCreateZone().  Then allocate memory with
   NSZoneMalloc().  Finally free memory with NSZoneFree(), and free a
   zone with NSDestroyZone().

   A Zone is initialized with a certain memory size, but will
   automagically grow if needed.  The incremental size of enlargement
   is set by the granularity flag.  A good choice for the initial
   memory size and the granularity is vm_page_size.

   Once of the options to NSCreateZone is the _canFree_ flag.  If this
   is YES, then you can use the NSZoneFree() function to reclaim
   memory.  If this is NO, then you cannot use NSZoneFree.  The only
   way then to free the memory is to destroy the entire zone.  This
   option allocates memory much quicker since it requires much less
   bookkeeping.

   NSZoneMalloc(), NSZoneCalloc() and NSZoneRealloc() each return a
   pointer to "size" bytes from zone "zonep".  The different flavors
   work the same as the malloc(), calloc() and realloc() c-library
   routines.

   NSCreateChildZone() and NSMergeZone() are not implemented.

   NSDefaultMemoryZone returns a NULL zone, which means the standard
   malloc zone.  NSZoneFree() frees memory within a
   zone. NSDestroyZone() deallocates the entire zone, including all
   allocated memory within it.  NSZoneFromPtr() finds a zone, given a
   pointer to memory.  The pointer must be one that was returned from
   NSZoneMalloc, or it can be zonep->base.  BXZonePtrInfo() returns
   debugging information for the ptr within a zone.  NSMallocCheck()
   returns 0 if the internal memory allocation is not corrupt, a
   positive integer otherwise. NSNameZone() assigns a name to a zone
   (less than 20 characters.).
  
   */

#include <gnustep/base/preface.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef __NeXT__
# include <malloc.h>
#endif
#include <Foundation/NSZone.h>
#include <gnustep/base/objc-malloc.h>

#define DEFAULTLISTSIZE 10

#define UNUSED    0
#define ALLOCATED 1
#define ZONELINK  2

#define WORDSIZE (sizeof(double))

typedef struct _chunkdesc 
{
    void *base;
    int  size;
    int  type;
} chunkdesc;



/* global variable */
llist ZoneList={0,0,sizeof(NSZone *),NULL};
char *memtype[]={"Unused","Allocated","Zone Link"};
char *freeStyle[]={"NO","Yes"};

/* local forward declarations. these are internal routines */
void *addtolist(void *ptr,llist *list,int at);
void delfromlist( llist *list, int at );
int searchheap(llist *heap,void *ptr);

/* things missing from malloc.h, so that gcc doesn't complain. */
/* Deal with bcopy: */
#if STDC_HEADERS || HAVE_STRING_H
#include <string.h>
/* An ANSI string.h and pre-ANSI memory.h might conflict.  */
#if !STDC_HEADERS && HAVE_MEMORY_H
#include <memory.h>
#endif /* not STDC_HEADERS and HAVE_MEMORY_H */
#define index strchr
#define rindex strrchr
#define bcopy(s, d, n) memcpy ((d), (s), (n))
#define bcmp(s1, s2, n) memcmp ((s1), (s2), (n))
#define bzero(s, n) memset ((s), 0, (n))
#else /* not STDC_HEADERS and not HAVE_STRING_H */
#include <strings.h>
/* memory.h and strings.h conflict on some systems.  */
#endif /* not STDC_HEADERS and not HAVE_STRING_H */

#ifdef HAVE_VALLOC
#include <stdlib.h>
#else
#define valloc 	malloc
#endif

/*
 * Returns the default zone used by the malloc(3) calls.
 */
NSZone *NSDefaultMallocZone(void)
{
#ifdef DEBUG
    printf("entered NSDefaultMallocZone\n");
#endif
    return NS_NOZONE;
}


/* 
 * Create a new zone with its own memory pool.
 * If canfree is 0 the allocator will never free memory and mallocing
 * will be fast.
 */
NSZone *NSCreateZone(size_t startSize, size_t granularity, int canFree)
{
    NSZone *ptr;
    chunkdesc temp;
    static int unique=0;
    
#ifdef DEBUG
    printf("entered NSCreateZone\n");
#endif
    ptr = (NSZone *) (*objc_malloc)(sizeof(NSZone));
    if (ptr == NULL) {
#ifdef DEBUG
        printf("out of memory for zone structure\n");
#endif
        return NS_NOZONE;
        }
    ptr->base        = (void *) (*objc_valloc)(startSize);
    if (ptr->base == NULL) {
#ifdef DEBUG
        printf("out of memory for zone\n");
#endif
        return NS_NOZONE;
        }
    
    ptr->size        = startSize;
    ptr->granularity = granularity;
    ptr->canFree     = canFree;
    ptr->parent      = NS_NOZONE;
    sprintf(ptr->name,"zone%d",unique++);
    ptr->heap.Count  = 0;
    ptr->heap.Size   = 0;
    ptr->heap.ElementSize = sizeof(chunkdesc);
    ptr->heap.LList   = NULL;

    temp.base   = ptr->base;
    temp.size   = startSize;
    temp.type   = UNUSED;
    addtolist(&temp,&(ptr->heap),0);

/* this might look funny, but I really want to pass the reference to the
   pointer ptr, and not the ptr it self, because of the way
   addtolist works. */
    addtolist(&ptr,&ZoneList,ZoneList.Count);
#ifdef DEBUG
    printf("zone '%s' created, ptr= %lx\n",ptr->name,(long)ptr);
#endif
   return ptr;
}

/*
 * Create a new zone who obtains memory from another zone.
 * Returns NS_NOZONE if the passed zone is already a child.
 */
NSZone  *NSCreateChildZone(NSZone *parentZone, size_t startSize,
                           size_t granularity, int canFree)
{
    NSZone *child;
/* Unfinished.  This will appear to do what it should do, but it won't.
 * Zone's give a nice improvement over malloc, but my gut feeling is
 * that child zones won't really improve much beyond that.  So I am
 * not implementing them.
 * These routines should 100% call-compatible with the
 * NeXTStep spec.  You can use NSMergeZone() like usual.
 */    

    child = NSCreateZone(startSize,granularity,canFree);
    child->parent = parentZone;

    return child;
}


/*
 * The zone is destroyed and all memory reclaimed.
 */
void NSDestroyZone(NSZone *zonep)
{
    int i,ok;
    chunkdesc *lastchunk;
    
#ifdef DEBUG
    printf("entered NSDestroyZone\n");
#endif

    ok = 0;
    for (i=0;i<ZoneList.Count;i++) {
#ifdef DEBUG
        printf("zone p = %lx list[%d]=%lx name='%s'\n",
               zonep,i,((NSZone **)ZoneList.LList)[i],
               ((NSZone **)ZoneList.LList)[i]->name);
#endif
        if (zonep == ((NSZone **)ZoneList.LList)[i]) {
            ok=1;
            break;
        }
    }
    
    if (ok) {
        lastchunk = &((chunkdesc *)zonep->heap.LList)[zonep->heap.Count-1];
        
        if (lastchunk->type ==  ZONELINK)
            NSDestroyZone((NSZone*)(lastchunk->base));
        free(zonep->base);
        free(zonep->heap.LList);
        delfromlist(&ZoneList,i);
        free(zonep);
    }
    else {
#ifdef DEBUG        
        printf("*** Zone not previously allocated\n");
#endif
    }
    return;
    
}

/*
 * Will merge zone with the parent zone. Malloced areas are still valid.
 * Must be an child zone.
 */
void NSMergeZone(NSZone *zonep)
{
    /* unfinished. */
    /* simply appends the child to the end of the list of zones from
       the parent. Only useful for compatibility. */
    NSZone *current;
    chunkdesc *chunk,new;
    int count;

    if (zonep->parent == NS_NOZONE) return;

    for(current = zonep->parent;
        count = current->heap.Count,
            chunk = &((chunkdesc *)current->heap.LList)[count-1],
            chunk->type == ZONELINK;
        current = chunk->base);

    new.base = zonep;
    new.size = 0;
    new.type = ZONELINK;
    addtolist(&new,&(current->heap),0);
    
    return;
}

void *NSZoneMalloc(NSZone *zonep, size_t size)
{
    int i,pages;
    size_t newsize,oddsize;
    void *ptr;
    chunkdesc temp,*chunk;
    NSZone *newzone;

    if (zonep == NS_NOZONE) return (*objc_malloc) (size);
/* round size up to the nearest word, so that all chunks are word aligned */
    oddsize = (size % WORDSIZE);
    newsize = size - oddsize + (oddsize?WORDSIZE:0);
/* if the chunks in this zone can be freed, then we have to scan the whole
   zone for chunks that have been deallocated for recycling. This requires
   extra time, so it is not as fast as !canFree */
    if (zonep->canFree) {
        for (i=0;i<zonep->heap.Count;i++) {
            chunk = &(((chunkdesc *)zonep->heap.LList)[i]);
            
            if (chunk->type == UNUSED) {
                if (newsize <= chunk->size) {
                    ptr = chunk->base;
                    chunk->type = ALLOCATED;
                    if (newsize < chunk->size) {
                        temp.base = chunk->base+newsize;
                        temp.size = chunk->size-newsize;
                        temp.type = UNUSED;
                        addtolist(&temp,&zonep->heap,i+1);
                        chunk->size = newsize;
                    }
                    return ptr;
                }
            }
            if (chunk->type == ZONELINK) {
#ifdef DEBUG
                printf("following link ...\n");
#endif
                return (NSZoneMalloc((NSZone *)(chunk->base),newsize));
            }
            
        }
    
    }
    else {
        chunk = &(((chunkdesc *)zonep->heap.LList)[0]);
        if (chunk->size > newsize) {
            ptr = chunk->base;
            chunk->size -=newsize;
            return ptr;
        }
        if (zonep->heap.Count == 2) {
            chunk = &(((chunkdesc *)zonep->heap.LList)[1]);
            if (chunk->type == ZONELINK) {
#ifdef DEBUG
                printf("following link ...\n");
#endif
                return (NSZoneMalloc((NSZone *)(chunk->base),newsize));
            }
        }
    }
    
#ifdef DEBUG        
    printf("*** no more memory in zone, creating link to new zone\n");
#endif                
    pages = newsize/(zonep->granularity)+1;
    newzone = NSCreateZone(pages*(zonep->granularity),
                           zonep->granularity,zonep->canFree);
    if (newzone == NS_NOZONE) {
#ifdef DEBUG
        printf("no memory left on system\n");
#endif
        return NULL;
    }
    
    temp.base = (void *)newzone;
    temp.size = 0;
    temp.type = ZONELINK;
    addtolist(&temp,&zonep->heap,zonep->heap.Count);
    return  (NSZoneMalloc(newzone,newsize));
    
}


void *NSZoneCalloc(NSZone *zonep, size_t numElems, size_t byteSize)
{
    void *ptr;

    ptr = NSZoneMalloc(zonep,numElems * byteSize);
    if (ptr) bzero(ptr,numElems*byteSize);
    return ptr;
    
}

void *NSZoneRealloc(NSZone *zonep, void *ptr, size_t size)
{
    int i,diff;
    void *ptr2;
    chunkdesc temp,*chunk,*nextchunk,*priorchunk;
    
    if (zonep == NS_NOZONE) return (*objc_realloc)(ptr,size);
    
    if (zonep->canFree) {
        i = searchheap(&(zonep->heap),ptr);
        
        if (i<0) return NULL;
        
        chunk = &((chunkdesc *)zonep->heap.LList)[i];
        if (chunk->type == ALLOCATED) {
            
            if (ptr == chunk->base) {
/* case 1: same size */
                if (size == chunk->size) {
                    chunk->type = ALLOCATED;
                    return ptr;
                }
                
/* case 2: smaller size */                
                if (size < chunk->size) {
                    temp.base = chunk->base+size;
                    temp.size = chunk->size-size;
                    temp.type = ALLOCATED;          /* the trick here is to
                                                       ALLOCATE this leftover,
                                                       and Free it, so that
                                                       the garbage collection
                                                       is done.*/
                    chunk->size = size;
                    addtolist(&temp,&zonep->heap,i+1);
                    NSZoneFree(zonep,temp.base);
                    return ptr;
                    
                }
/* case 3: larger size */                
/* case 3a: larger size, but there is enough free memory immediately after */
                if (i+1<zonep->heap.Count) {
                    nextchunk = &((chunkdesc *)zonep->heap.LList)[i+1];
                    if (nextchunk->type == UNUSED) 
                        if (size <= chunk->size + nextchunk->size) {
                            diff = size - chunk->size;
                            chunk->size = size;
                            if (diff != nextchunk->size) {
                                nextchunk->base += diff;
                                nextchunk->size -= diff;
                            }
                            else {
                                delfromlist(&zonep->heap,i+1);
                            }
                            
                            return ptr;
                        }
                }
                
/* case 3b: larger size, but there is enough free memory immediately before */
                if (i-1>=0) {
                    priorchunk = &((chunkdesc *)zonep->heap.LList)[i-1];
                    if (priorchunk->type == UNUSED) 
                        if (size < chunk->size + priorchunk->size) {
                            ptr = priorchunk->base;
                            diff = size - priorchunk->size;
                            if (diff != chunk->size) {
                                chunk->base += diff;
                                chunk->size -= diff;
                                chunk->type = UNUSED;
                            }
                            else {
                                delfromlist(&zonep->heap,i-1);
                            }
                            priorchunk->size = size;
                            priorchunk->type = ALLOCATED;
                            bcopy(chunk->base,ptr,chunk->size);
                            return ptr;
                        }
                }
                
                
/* case 3c: larger size, but there is enough free memory immediately
   before+after */
                if (i+1<zonep->heap.Count && i-1>=0) {
                    nextchunk = &((chunkdesc *)zonep->heap.LList)[i+1];
                    priorchunk = &((chunkdesc *)zonep->heap.LList)[i-1];
                    if (nextchunk->type == UNUSED &&
                        priorchunk->type == UNUSED) 
                        if (size <=
                            chunk->size+nextchunk->size+priorchunk->size) {
                            priorchunk->type = ALLOCATED;
                            ptr = priorchunk->base;
                            diff = size - chunk->size - priorchunk->size;
                            if (diff != nextchunk->size) {
                                
                                chunk->base += diff;
                                chunk->size -= diff;
                                chunk->type  = UNUSED;
                                delfromlist(&zonep->heap,i+1);
                            }
                            else {
                                delfromlist(&zonep->heap,i+1);
                                delfromlist(&zonep->heap,i);
                            }
                            
                            priorchunk->size = size;
                            bcopy(chunk->base,ptr,chunk->size);
                            
                            return ptr;
                        }
                }
                
/* case 3d: larger size, have to relocate far away */
                ptr2 = ptr;
                ptr = NSZoneMalloc(zonep,size);
                bcopy(ptr2,ptr,size);
                NSZoneFree(zonep,ptr2);
                return ptr;
            }
        }
#ifdef DEBUG
        printf("*** original malloc info not found\n");
#endif    
        return NULL;
    }
    else {
#ifdef DEBUG
        printf("*** can't use NSZoneRealloc with !canFree\n");
#endif
        return NULL;
    }
    
}

void NSZoneFree(NSZone *zonep, void *ptr)
{
    int i;
    chunkdesc *chunk,*otherchunk;

    
    if (zonep == NS_NOZONE) {
        free(ptr);
        return;
    }
    
    if (zonep->canFree) {
        i = searchheap(&(zonep->heap),ptr);
        if (i<0) {
#ifdef DEBUG
            printf("*** block not found for NSZoneFree\n");
            
#endif
            if ((zonep = NSZoneFromPtr(ptr)) == NS_NOZONE) {
                return;
            }
            i = searchheap(&(zonep->heap),ptr);
            return;
        }
        
    
        chunk = &((chunkdesc *)zonep->heap.LList)[i];
        if (chunk->type == ALLOCATED) {
            chunk->type = UNUSED;
/* combine with upper free block */
            if (i+1<zonep->heap.Count) {
                otherchunk = &((chunkdesc *)zonep->heap.LList)[i+1];
                if (otherchunk->type == UNUSED) {
                    chunk->size += otherchunk->size;
                    delfromlist(&zonep->heap,i+1);
                }
            }
            
/* combine with lower free block */
            if (i-1>=0) {
                otherchunk = &((chunkdesc *)zonep->heap.LList)[i-1];
                if (otherchunk->type == UNUSED) {
                    otherchunk->size += chunk->size;
                    delfromlist(&zonep->heap,i);
                }
            }
            
        }
        else
#ifdef DEBUG
            printf("*** original malloc info not found\n");
#endif
        return;
    }
    
    else {
#ifdef DEBUG
        printf("*** can't use NSZoneFree with !canFree\n");
#endif
        return;
    }
}

/*
 * Returns the zone for a pointer.
 * NS_NOZONE if not in any zone.
 * The ptr must have been returned from a malloc or realloc call.
 */
NSZone *NSZoneFromPtr(void *ptr)
{
    int i;
    
    for (i=0;i<ZoneList.Count;i++) {
        if (searchheap(&(((NSZone **)ZoneList.LList)[i]->heap),ptr) != -1)
            return (NSZone *) ((NSZone **)ZoneList.LList)[i];
    }
    return NS_NOZONE;
}

/*
 * Debugging Helpers.
 */
 
 /*  
  * Will print to stdout information about the pointer, and all the others
  * in the same zone.
  */
void NSZonePtrInfo(void *ptr)
{
    NSZone *tmp,*z;
    chunkdesc *chunk;
    int i;
    
    if (ZoneList.Count == 0) {
        printf("NSZONE: No zones in memory.\n");
        return;
    }
    
    tmp = NSZoneFromPtr(ptr);
    printf("NSZONE: pointer = %lx, zone and chunk marked with * below\n",
           (long) ptr);
    printf(
        "NSZONE: ZONE  [ #] _Pointer __Parent ____Base ____Size Granular free? Name\n");
    
    for (i=0;i<ZoneList.Count;i++) {
        z = ((NSZone **)ZoneList.LList)[i];
        printf("NSZONE: ZONE %c[%2d] %8lx %8lx %8lx %8lx %8lx %-5s '%s'\n",
               (tmp==z)?'*':' ',
               i,
               (long)z,
               (long)z->parent,
               (long)z->base,
               (long)z->size,
               (long)z->granularity,
               freeStyle[z->canFree],
               z->name);
    }
    
    if (tmp != NS_NOZONE) {
        printf("NSZONE: CHUNK - [ #] ____Base ____Size Type (Zone=%lx)\n",
               (long)tmp);
        for (i=0;i<tmp->heap.Count;i++) {
            
            chunk = &((chunkdesc *)tmp->heap.LList)[i];
            printf("NSZONE: CHUNK %c [%2d] %8lx %8lx %s\n",
                   (ptr==chunk->base)?'*':' ',
                   i,
                   (long)chunk->base,
                   (long)chunk->size,
                   memtype[chunk->type]);
        }
        
    }
    
    return;
}

/*
 * Will verify all internal malloc information.
 * This is what malloc_debug calls.
 */
int NSMallocCheck(void)
{
    NSZone *tmp;
    void *base,*currentbase,*zbase;
    int i,j,k,lasttype,type,ok;
    size_t size,zsize;
    
    
/*    if (ZoneList == NULL) return 10; */

    for (i=0;i<ZoneList.Count;i++) {
        tmp = (((NSZone **)ZoneList.LList)[i]);
    
        if (tmp == NS_NOZONE) {
#ifdef DEBUG
            printf("error 1: null zone in ZoneList\n");
#endif            
            return 1;
        }
        
        if (tmp->heap.Count < 1) {
            
#ifdef DEBUG
            printf("error 2: Heap contains %d blocks\n",tmp->heap.Count);
#endif        
            return 2;
        }

        if (tmp->parent != NS_NOZONE) {
            ok = 0;
            for (k=0;k<ZoneList.Count;k++)
                if (tmp->parent == &((NSZone *)ZoneList.LList)[k])
                    ok = 1;
            if (ok==0) {
#ifdef DEBUG
            printf("error 3: parent zone=%lx doesn't exist\n",tmp->parent);
#endif        
                return 3;
            }
        }
        
        zbase = tmp->base;
        zsize = tmp->size;
        currentbase = ((chunkdesc *)tmp->heap.LList)[0].base;
        lasttype = ALLOCATED;
        for (j=0;j<tmp->heap.Count;j++) {
            base = ((chunkdesc *)tmp->heap.LList)[j].base;
            size = ((chunkdesc *)tmp->heap.LList)[j].size;
            type = ((chunkdesc *)tmp->heap.LList)[j].type;

            if (base<zbase || base>=zbase+zsize ||
                size>zsize || base+size>=zbase+zsize) {
#ifdef DEBUG
                printf("error 7: funny chunk addresses:base=%lx size=%lx\n",
                       (long)base,(long)size);
#endif
                return 7;
            }
            
            if (type == ZONELINK) {
                ok = 0;
                for (k=0;k<ZoneList.Count;k++)
                    if (base == ((NSZone *)ZoneList.LList)[k].base)
                        ok = 1;
                if (ok==0) {
#ifdef DEBUG
            printf("error 4: zone to link=%lx doesn't exist\n",base);
#endif        
                    return 4;
                }
                continue;
            }
            
            if (base != currentbase) {
#ifdef DEBUG
                printf("error 5: blocks are not contiguous.\n");
#endif                
                return 5;
            }
            if (lasttype == UNUSED && type == UNUSED) {
#ifdef DEBUG
                printf("error 6: two consecutive UNUSED blocks\n");
                return 6;
#endif
            }
            lasttype = type;
            currentbase += ((chunkdesc *)tmp->heap.LList)[j].size;
        }
        
    }
    return 0;
}

/*
 * Give a zone a name.
 *
 * The string will be copied.
 */
    void NSNameZone(NSZone *zonep, const char *name)
{
#ifdef DEBUG
    printf("current name=%s changing to %s\n",zonep->name,name);
#endif
    if (zonep != NULL) {
        strncpy(zonep->name,name,MAXZONENAMELENGTH);
        zonep->name[MAXZONENAMELENGTH]=0;
    }
    return;
}

void
NSSetZoneName (NSZone *z, NSString *name)
{
  /* xxx Not implemented. */
  abort ();
}

NSString *NSZoneName (NSZone *z)
{
  /* xxx Not implemented. */
  abort ();
  return NULL;
}


/* these are internal routines, not to be called by the user.
   They manipulate pseudo List objects, but with less overhead.
   */

void *addtolist(void *ptr,llist *list, int at)    
{
    void *newelement;
    
    /* increase allocated size for list if necessary */
    if (list->Count>= list->Size) {
        if (list->LList == NULL) {
            list->Size = DEFAULTLISTSIZE;
            list->LList = (void *)
	      (*objc_malloc)(list->ElementSize * list->Size);
        }
        else {
            list->Size *= 2;
            list->LList = (void *)
	      (*objc_realloc)(list->LList, list->ElementSize * list->Size);
        }
        
    }
    
    newelement = &((char *)list->LList)[at*list->ElementSize];
    
    /* add element to list at position at */
    if (at != list->Count)
        bcopy(&((char *)list->LList)[at*list->ElementSize],
              &((char *)list->LList)[(at+1)*list->ElementSize],
              list->ElementSize*(list->Count - at)
            );
    bcopy(ptr,newelement,list->ElementSize);
    list->Count++;
    return newelement;
    
}


void delfromlist( llist *list, int at )
{
    if (at+1<list->Count)
        bcopy(&((char *)list->LList)[(at+1)*list->ElementSize],
              &((char *)list->LList)[(at  )*list->ElementSize],
              list->ElementSize*(list->Count - at-1));
              
    list->Count--;
}

/* this searches the heap linearly.. someone can change this to
   a binary search if they want. */

int searchheap(llist *heap,void *ptr)
{
    int i;
    
    for (i=0;i<heap->Count;i++)
        if (ptr == ((chunkdesc *)heap->LList)[i].base) return i;

    return -1;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.