#include #include "jmalloc.hpp" #include #include "macs.hpp" #include #include "exitproc.hpp" #include "dprint.hpp" #ifdef __WATCOMC__ #include #include char *WAT_dos_alloc(long &size) { union REGS r; r.x.eax=0x0100; r.x.ebx=(size+15)>>4; int386(0x31,&r,&r); if (r.x.cflag) { size=(long)r.w.bx*16; return NULL; } else return (char *)((r.x.eax&0xffff)<<4); } void WAT_dos_free(void *ptr) { union REGS r; r.x.eax=0x0101; r.x.edx=((long)ptr)>>4; printf("free : segment is %d\n",r.w.dx); int386(0x31,&r,&r); if (r.x.cflag) printf("DOS_free failed\n"); } #endif struct memory_node { memory_node *next; long size; #ifdef MEM_CHECK char *name; // name is allocated on regular heap #endif // because it is used for debugging purposes // and will probably be run on my linux box with VMM memory_node *next_free; // if free (size<0) this is a pointer to the next free block // otherwise data starts here }; struct memory_block { int type; memory_node *fnode; } ; #define JM_SMALL_SIZE 128 // above 128 bytes is considered to be a big block and no hashing is done memory_node *jm_small[JM_SMALL_SIZE/4]; memory_node *jm_big; memory_node *last_used=NULL; #define MAX_BLOCKS 4 long mem_blocks=0; memory_block blocks[MAX_BLOCKS]; #define REG_MEM 1 #define LOW_MEM 2 #define STATIC_MEM 3 #ifdef MEM_CHECK void mem_check() { int i,j; for (i=0;inext;n;n=n->next,j++) { if (last>=n) { printf("Memory corrupted, block #%d\n",j); if (last->size>=0) printf("last block name is %s\n",last->name); else printf("last block was free\n"); mem_report("corrupt"); } } } } #endif void add_block(void *addr, long size, int type) { if (mem_blockssize=-size+sizeof(memory_node)-sizeof(memory_node *); f->next=NULL; f->next_free=jm_big; jm_big=f; mem_blocks++; } else fprintf(stderr,"added more than MEM_BLOCKS blocks\n"); } void *operator new( size_t size) { return jmalloc(size,"::new object"); } void operator delete(void *ptr) { jfree(ptr); } void jmem_cleanup(int ret, void *arg) { jmalloc_uninit(); } void jmalloc_init(long min_size) // allocates as much memory as possible, craps if min_size too big { if (mem_blocks) dprintf("warning : jmalloc_init called twice\n"); else { memset(jm_small,0,sizeof(memory_node *)*JM_SMALL_SIZE/4); // clear out old free stacks jm_big=NULL; exit_proc(jmem_cleanup,jmalloc_uninit); // make sure memory gets freed up on exit void *mem_start; long mem_size; mem_start=NULL; for (mem_size=4000000;!mem_start && mem_size>0x4000;mem_size-=0x100) // allocate 4 MB mem_start=malloc(mem_size); if (mem_start) { free(mem_start); mem_size-=0x4000; mem_start=malloc(mem_size); // save some space on regular heap dprintf("Memory subsystem : added high mem block (%d bytes)\n",mem_size); add_block(mem_start,mem_size,REG_MEM); } #ifdef __WATCOMC__ // allocate low memory from DOS long dos_size=0xA0000; char *dmem=(char *)WAT_dos_alloc(dos_size); if (dmem) { printf("expecting dos_alloc to fail for %d bytes\n",0xa0000); } if (dos_size<12000) dprintf("Memory subsystem : low memory not used.. only %d bytes available\n",dos_size); else { dos_size-=10000; // 10k in case we need to for something else dmem=(char *)WAT_dos_alloc(dos_size); if (dmem) { add_block(dmem,dos_size,LOW_MEM); dprintf("Memory subsystem : using %d bytes of low memory\n",dos_size); } else dprintf("error in jmalloc_init\n"); } #endif if (j_available()size<0) { if (!f->next || f->next->size>0) // if next bock is not free and to stack { if (-f->sizenext_free=jm_small[-f->size/4]; jm_small[-f->size/4]=f; } else { f->next_free=jm_big; jm_big=f; } f=f->next; } else if (f->next && f->next->size<0) { f->size+=f->next->size-sizeof(memory_node)+sizeof(memory_node *); f->next=f->next->next; j=1; } } else f=f->next; } } return j; } void *jmalloc(long size, char *what_for) { if (!mem_blocks) // if not initialized, then use real malloc return malloc(size); #ifdef MEM_CHECK if (size<=0) { size=4; printf("jmalloc : asking for 0 or less\n"); } #endif size=(size+3)&(0xffffffff-3); // make sure the size is word alligned while (1) // loop until we find a block to return { if (sizesize=-find->size; // mark as being used #ifdef MEM_CHECK find->name=strcpy((char *)malloc(strlen(what_for)+1),what_for); #endif jm_small[size/4]=find->next_free; // pop the block from the free stack return (void *)&find->next_free; } else { // find first block which will accomodate this size // save the last pointer so we can compact the stack memory_node *find=NULL,*f,*last=NULL; for (f=jm_big;!find && f;f=f->next_free) if (-f->size>=size) find=f; else last=f; if (find) { find->size=-find->size; // mark as being used #ifdef MEM_CHECK find->name=strcpy((char *)malloc(strlen(what_for)+1),what_for); #endif if (last) last->next_free=find->next_free; else jm_big=find->next_free; // pop the block from the free stack if (find->size-size>sizeof(memory_node)) // enough space for free block? { memory_node *new_free=(memory_node *)(((char *)(&find->next_free))+size); new_free->size=(find->size+sizeof(memory_node *)-size-sizeof(memory_node)); find->size=size; if (new_free->sizenext_free=jm_small[new_free->size/4]; jm_small[new_free->size/4]=new_free; } else { new_free->next_free=jm_big; jm_big=new_free; } new_free->next=find->next; find->next=new_free; new_free->size=-new_free->size; // mark this block as free } return (void *)&find->next_free; } else if (!join_blocks()) free_up_memory(); } } } /* // start at the last spot we used and see if we can find a block near there if (last_used && (last_used->size<0) && ((-last_used->size-reserve)>=size)) f=last_used; if (!f) // no block yet, scan for one. { int i; for (i=0;!f && isize>=0 || -f->size-reserve<=size);f=f->next); } } if (!f && !join_blocks()) free_up_memory(); // user defined function to free memory else { if (size>=-f->size-reserve) // allocating the whole block? { f->size=-f->size; // chain stays the same, but mark memory as used #ifdef MEM_CHECK f->name=strcpy((char *)malloc(strlen(what_for)+1),what_for); #endif last_used=f->next; // use next spot as refrence spot } else // else create a new free node { memory_node *new_free; new_free=(memory_node *)(((unsigned char *)f)+size+sizeof(memory_node)); new_free->next=f->next; new_free->size=f->size+size+sizeof(memory_node); f->next=new_free; f->size=size; last_used=new_free; #ifdef MEM_CHECK f->name=strcpy((char *)malloc(strlen(what_for)+1),what_for); #endif } return (void *)(((unsigned char *)(f))+sizeof(memory_node)); } } return NULL; // while never happen } */ void jfree(void *ptr) { if (!mem_blocks) free(ptr); else { memory_node *f=(memory_node *)(((char *)ptr)+sizeof(memory_node *)-sizeof(memory_node)); #ifdef MEM_CHECK if (f->size<0) { printf("Bad pointer\n"); return ; } free(f->name); #endif if (f->sizenext_free=jm_small[f->size/4]; jm_small[f->size/4]=f; } else { f->next_free=jm_big; jm_big=f; } f->size=-f->size; // mark free and join blocks later } } void *jrealloc(void *ptr, long size, char *what_for) { if (!mem_blocks) { if (ptr) // some platforms don't do this! return realloc(ptr,size); else return malloc(size); } if (!ptr) return jmalloc(size,what_for); else { if (size==0) // if the new size needed is zero then we can throw this away. { jfree(ptr); return NULL; } else { memory_node *f=(memory_node *)(((char *)ptr)+sizeof(memory_node *)-sizeof(memory_node)); long old_size=f->size; // for now we are not going to be very smart about our re-allocation // i.e. just allocate another block and copy this into it. void *new_loc=jmalloc(size,what_for); if (size>old_size) memcpy(new_loc,ptr,old_size); else memcpy(new_loc,ptr,size); jfree(ptr); return new_loc; } } return NULL; } long j_allocated() { memory_node *f; long s=0,i; for (i=0;inext) { if (f->size>0) s+=f->size; } } return s; } long j_available() { memory_node *f; long s=0,i; for (i=0;inext) { if (f->size<0) s+=-f->size; } } return s; } void mem_report(char *filename) { long tot=0; FILE *fp=fopen(filename,"wb"); int size_count[64]; memset(size_count,0,sizeof(size_count)); if (fp) { long i,size,tblocks=0; for (i=0;isize>=0) { tblocks++; if (p->size<64) size_count[p->size]++; } if (p==last_used) fprintf(fp,"-> "); #ifdef MEM_CHECK if (p->size<0) fprintf(fp,"%10d %d %s\n",offset,p->size,"FREE"); else fprintf(fp,"%10d %d %s\n",offset,p->size,p->name); #else fprintf(fp,"%10d %d\n",offset,p->size); #endif offset+=(abs(p->size)+sizeof(memory_node)); tot+=abs(p->size); p=p->next; } } fprintf(fp,"## Total = %d bytes\n",tot); fprintf(fp,"## Total allocated = %d bytes\n",j_allocated()); fprintf(fp,"## Total blocks = %d\n",tblocks); for (i=0;isize); int tb=0; for (int j=0;jnext) if (p->size==i*4) tb++; } fprintf(fp,"%d, Used = %d\n",t,tb); } } } fclose(fp); }