/*code copyright by Gabriel Sechan. This code is disributed under the GPL, with the addition that this source code may not be redistributed for charge. All comments, bug fixes, great new ideas, additions you want in it, additions you have written in it and wish to spread, money, death threats, proposals of marriage (attractive females only), less formal proposals (same disclaimer), and ritualised worship of me can bve sent to gsechan@hotmail.com */ #include "debuglib.h" //LibLinkedList functions //First //Move the current ptr to the head of the list template void LibLinkedList::First(){ current=head; } //Get //Form 1: Return the data held at the current ptr in out //Form 2: Return the data held at the given number in out. Uses 0 based counting // return values: 1=success, 0=error or null list, -1=list too short(Form 2) template int LibLinkedList::Get(T& out){ if(current==NULL) return 0; out=current->data; return 1; } template int LibLinkedList::Get(T& out,int num){ if(head==NULL) return 0; if(num>=size || num<0) return -1; Node *temp=head; for(int x=0;xnext; } out=temp->data; return 1; } //Goto //Moves the current ptr to the numbered item. If list is too short, it doesnt move template void LibLinkedList::Goto(int num){ if(head==NULL || num>=size || num<0) return; for(int x=0;xnext; } /*InsertAfter Form 1:Inserts the given data after the current pointer. Current ptr moves to new element. Form 2:Inserts the given data after the numbered element. If list is too small, it appends to the end. Current ptr does not move. returns 1 on success, 0 on error */ template int LibLinkedList::InsertAfter(T newData){ if(head==NULL){ // empty list case head=(Node*)malloc(sizeof( Node)); if(head==NULL) return 0; head=(Node*)memset((void*)head,0,sizeof(Node)); head->prev=NULL; head->next=NULL; head->data=newData; current=tail=head; size=1; return 1; } if(current==tail){ //special case 2: inserting into tail current=(Node*)malloc(sizeof( Node)); if(current==NULL) return 0; current=(Node*)memset((void*)current,0,sizeof(Node)); current->data=newData; current->prev=tail; current->next=NULL; tail->next=current; tail=current; size++; return 1; } Node* temp=(Node*)malloc(sizeof( Node)); if(temp==NULL) return 0; temp=(Node*)memset((void*)temp,0,sizeof(Node)); temp->data=newData; temp->prev=current; temp->next=current->next; current->next->prev=temp; current->next=temp; current=temp; size++; return 1; } template int LibLinkedList::InsertAfter(T newData,int num){ if(head==null){ // empty list case head=(Node*)malloc(sizeof( Node)); if(head==NULL) return 0; head=(Node*)memset((void*)head,0,sizeof(Node)); head->prev=NULL; head->next=NULL; head->data=newData; current=tail=head; size=1; return 1; } if(num>=size-1){ //special case 2: inserting into tail Node* temp=(Node*)malloc(sizeof( Node)); if(temp==NULL) return 0; temp=(Node*)memset((void*)temp,0,sizeof(Node)); temp->data=newData; temp->prev=tail; temp->next=NULL; tail->next=temp; tail=temp; size++; return 1; } Node* temp=(Node*)malloc(sizeof( Node)); if(temp==NULL) return 0; temp=(Node*)memset((void*)temp,0,sizeof(Node)); Node* temp2=head; for(int x=0;xnext; temp->data=newData; temp->prev=temp2; temp->next=temp2->next; temp2->next->prev=temp; temp2->next=temp; size++; return 1; } /*InsertBefore Form 1:Inserts the given data before the current pointer. Current ptr moves to new element. Form 2:Inserts the given data before the numbered element. If list is too small, it appends to the head. Current ptr does not move. returns 1 on success, 0 on error */ template int LibLinkedList::InsertBefore(T newData){ if(head==NULL){ // empty list case head=(Node*)malloc(sizeof( Node)); if(head==NULL) return 0; head=(Node*)memset((void*)head,0,sizeof(Node)); head->prev=NULL; head->next=NULL; head->data=newData; current=tail=head; size=1; return 1; } if(current==head){ //special case 2: inserting into head current=(Node*)malloc(sizeof( Node)); if(current==NULL) return 0; current=(Node*)memset((void*)current,0,sizeof(Node)); current->data=newData; current->prev=NULL; current->next=head; head->prev=current; head=current; size++; return 1; } Node* temp=(Node*)malloc(sizeof( Node)); if(temp==NULL) return 0; temp=(Node*)memset((void*)temp,0,sizeof(Node)); temp->data=newData; temp->next=current; temp->prev=current->prev; current->prev->next=temp; current->prev=temp; current=temp; size++; return 1; } template int LibLinkedList::InsertBefore(T newData,int num){ if(head==NULL){ // empty list case head=(Node*)malloc(sizeof( Node)); if(head==NULL) return 0; head=(Node*)memset((void*)head,0,sizeof(Node)); head->prev=NULL; head->next=NULL; head->data=newData; current=tail=head; size=1; return 1; } if(num<=0){ //special case 2: inserting into head Node* temp=(Node*)malloc(sizeof( Node)); if(temp==NULL) return 0; temp=(Node*)memset((void*)temp,0,sizeof(Node)); temp->data=newData; temp->next=head; temp->prev=NULL; head->prev=temp; head=temp; size++; return 1; } Node* temp=(Node*)malloc(sizeof( Node)); if(temp==NULL) return 0; temp=(Node*)memset((void*)temp,0,sizeof(Node)); Node* temp2=head; for(int x=0;xnext; temp->data=newData; temp->next=temp2; temp->prev=temp2->prev; temp2->prev->next=temp; temp2->prev=temp; size++; return 1; } //Last //moves the current ptr to the tail of the list template void LibLinkedList::Last(){ current=tail; } //Next //moves the current ptr 1 forward, If at tail, stays there template void LibLinkedList::Next(){ if(current==NULL || current->next==NULL) return; current=current->next; } //Prev //moves the current ptr 1 back, If at head, stays there template void LibLinkedList::Prev(){ if(current==NULL || current->prev==NULL) return; current=current->prev; } /* Remove Form 1:Removes the element at the current ptr. Current moves to the next item Form 2:Removes the element at the given number. If number out of range, none removed. If current ptr removed, current pts to next item. For both if current is moved and it is in the tail, it goes back instead of forwards return values: 1=success 0=error -1=out of range */ template int LibLinkedList::Remove(){ if(current==NULL) //empty list return -1; size--; if(head==tail){ //special case: 1 thing in list head->data.~T(); free(head); head=tail=current=NULL; return 1; } if(head==current){ //special case: remove head head=current->next; current->data.~T(); free(current); head->prev=NULL; current=head; return 1; } if(tail==current){ //special case: remove tail tail=tail->prev; current->data.~T(); free(current); tail->next=NULL; current=tail; return 1; } Node *temp=current->next; //normal case temp->prev=current->prev; current->prev->next=temp; current->data.~T(); free(current); current=temp; return 1; } template int LibLinkedList::Remove(int num){ if(current==NULL || num<0 || num>=size) return -1; size--; if(head==tail){ //special case: 1 thing in list head->data.~T(); free(head); head=tail=current=NULL; return 1; } if(num==0){ //special case: remove head Node *temp=head; head=head->next; if(current==temp) current=head; temp->data.~T(); free(temp); head->prev=NULL; return 1; } if(num==size){ //special case: remove tail Node *temp=tail; tail=tail->prev; if(current==temp) current=tail; temp->data.~T(); free(temp); tail->next=NULL; return 1; } Node *temp2=head; for(int x=0;xnext; if(current==temp2) current=temp2->next; Node *temp=temp2->next; //normal case temp->prev=temp2->prev; temp2->prev->next=temp; temp2->data.~T(); free(temp2); return 1; } //Size //returns the number of elements in the list template int LibLinkedList::Size(){ return size; } //constructors and rule of big 3 functions template LibLinkedList::LibLinkedList(){ head=tail=current=NULL; size=0; } template LibLinkedList::LibLinkedList(LibLinkedList& in){ CopyAll(in); } template LibLinkedList::~LibLinkedList(){ DeleteAll(); } template LibLinkedList& LibLinkedList::operator= (LibLinkedList& in){ DeleteAll(); CopyAll(in); return *this; } //DeleteAll //helper function for big 3. Must delete ALL dynamic memory for the class template void LibLinkedList::DeleteAll(){ Node *temp=head; if(head==NULL) //null list return; do{ current=temp->next; delete temp; temp=current; }while(temp!=NULL); head=tail=current=NULL; } //CopyAll //helper funcion to copy all data from one class to another. Must realloc dynamic memory template void LibLinkedList::CopyAll(LibLinkedList& in){ Node *intemp=in.head; Node *temp,*prevtemp=NULL; size=in.size; if(size==0){ head=tail=current=NULL; return; } do{ temp=new Node; if(temp==NULL) return; temp->data=intemp->data; temp->prev=prevtemp; if(prevtemp!=NULL) prevtemp->next=temp; temp->next=NULL; prevtemp=temp; intemp=intemp->next; }while (intemp!=NULL); current=head; intemp=in.head; for(int x=0;xnext; intemp=intemp->next; } } /***************************************************************/ //NamedPtr class functions //Alloc //Called when the ptr with the given name is allocated. Updates total stats void NamedStats::Alloc(int amt){ last=1; if(amt<0) return; amountAllocated+=amt; numAllocs++; } //Dealloc() //Called whan a named ptr is deallocated. Updates total stats void NamedStats::Dealloc(){ last=0; numDeallocs++; } // get functions char* NamedStats::GetName(){ return name; } unsigned int NamedStats::GetNumAllocs(){ return numAllocs; } unsigned int NamedStats::GetNumDeallocs(){ return numDeallocs; } unsigned int NamedStats::GetTotalAllocs(){ return amountAllocated; } int NamedStats::GetLast(){ return last; } //constructors and rule of Big 3 NamedStats::NamedStats(){ last=0; numAllocs=numDeallocs=amountAllocated=0; name=NULL; } NamedStats::NamedStats(char* str){ numAllocs=numDeallocs=amountAllocated=0; last=0; if(str==NULL) name=NULL; else{ name=(char*)malloc(strlen(str)+1); memcpy(name,str,strlen(str)+1); } } NamedStats::NamedStats(NamedStats& in){ CopyAll(in); } NamedStats& NamedStats::operator =(NamedStats& in){ DeleteAll(); CopyAll(in); return *this; } NamedStats::~NamedStats(){ DeleteAll(); } //DeleteAll //helper function for big 3, delete all dynamic memory void NamedStats::DeleteAll(){ if(name!=NULL) free(name); name=NULL; } //CopyAll //helper function for big 3, copies 1 insance into another void NamedStats::CopyAll(NamedStats& in){ last=in.last; amountAllocated=in.amountAllocated; numAllocs=in.numAllocs; numDeallocs=in.numDeallocs; if(in.name==NULL) name=NULL; else{ name=(char*)malloc(strlen(in.name)+1); memcpy(name,in.name,strlen(in.name)+1); } } /*****************************************************/ //PtrData funcs //Get functions void* PtrData::GetData(){ return data; } char* PtrData::GetName(){ return name; } unsigned int PtrData::GetSize(){ return size; } //constructors and rule of Big 3 funcs PtrData::PtrData(){ data=NULL; size=0; name=NULL; } PtrData::PtrData(void *info,int sz,char* str){ data=info; size=sz; if(str==NULL) name=NULL; else{ name=(char*)malloc(strlen(str)+1); memcpy(name,str,strlen(str)+1); } } PtrData::PtrData(PtrData& in){ CopyAll(in); } PtrData& PtrData::operator =(PtrData& in){ DeleteAll(); CopyAll(in); return *this; } PtrData::~PtrData(){ DeleteAll(); } //DeleteAll //helper function to delete all dynamic data void PtrData::DeleteAll(){ if(name!=NULL) free(name); } //CopyAll //helper function to copy class void PtrData::CopyAll(PtrData& in){ data=in.data; size=in.size; if(in.name==NULL) name=NULL; else{ name=(char*)malloc(strlen(in.name)+1); memcpy(name,in.name,strlen(in.name)+1); } } /*********************************************************/ //finally, the actual library functions //Global Data. Sorry, it cant be helped, the data is needed by 3-4 different functions LibLinkedList NamedPtrData; //list of all named ptrs and their stats LibLinkedList AllocatedPtrs; //a list of all allocated ptrs LibLinkedList FunctionStack; //names of all functions currently in. ostream *LogStream; //stream to write all outputs to FILE *LogFileHandle; //handle to write all file outputs to int DebugLibOptions=0; //the options selected in the debug library Currently- -1= not set, 0=use FILE* 1=use stream int OutputType=-1; unsigned int totalAllocation=0,numAllocations=0,numDeallocations=0; /*WrapperMalloc Wraps the malloc call for non debug builds. returns 1 if ptr non-null, 0 if null (mnow you dont have to check Form 1: Takes a VOIDPTR as a argument, returns an error value Form 2: Returns the void*, no error code */ int WrapperMalloc(unsigned int amt,VOIDPTR data,char* name){ data=malloc(amt); return data!=NULL; } void* WrapperMalloc(unsigned int amt,char* name){ return malloc(amt); } //WrapperFree //Wraps the free call for non debug builds. Only calls free if ptr non null //Returns 1 if ptr was non-null int WrapperFree(void* data,char* name){ int ret=data!=NULL; if(data!=NULL) free(data); return ret; } /*InitDebugLib Initializes the debug library and sets the options wanted. If it is not called, the library is not promised to work properly. The options param is used for future compatibility and should be set to 0 for now. Form 1: Initialises lib and uses a FILE* for output Form 2: Initialises lib and uses a stream for output. In both cases you are expected to pass a valid stream or FILE and have already done any initialization work. Returns 1 on success, 0 on failure. */ int InitDebugLibOn(FILE* file,int options){ OutputType=0; LogFileHandle=file; LogStream=NULL; return 1; } int InitDebugLibOn(ostream *file,int options){ OutputType=1; LogStream=file; LogFileHandle=NULL; return 1; } int InitDebugLibOff(FILE* file,int options){ return 1; } int InitDebugLibOff(ostream *file,int options){ return 1; } /*FreeDebugLib Frees any resources used by the debug library. Outputs total statistics and pointer name stats for the program. */ void FreeDebugLib(){ int x,len; ShowDebugStatistics(); numAllocations=numDeallocations=DebugLibOptions=totalAllocation=0; OutputType=-1; len=NamedPtrData.Size(); for( x=0;xflush(); } NamedPtrData.First(); int num=NamedPtrData.Size(); for(int x=0;xflush(); } NamedPtrData.Next(); } } void ShowDebugStatistics(char* name){ if(name==NULL){ if(OutputType==0){ fprintf(LogFileHandle,"\n\n\nTotal Stats\nNumber of Allocations:%d\nAmount Allocated:%d\nNumber of Deallocations:%d\nMissed deallocations:%d\n",numAllocations,totalAllocation,numDeallocations,numAllocations-numDeallocations); fflush(LogFileHandle); return; } if(OutputType==1){ (*LogStream)<<"\n\n\nTotal Stats\nNumber of Allocations:"<flush(); return; } return; } NamedPtrData.First(); int num=NamedPtrData.Size(); for(int x=0;xflush(); NamedPtrData.Last(); return; } } NamedPtrData.Next(); } } /*EnterFunc Tells the library we have entered a new function to debug. Used to tell when errors occured. I suggest you call the macro for this whenever you enter a function. Remember you must call the reverse before ALL returns for it to work propperly */ void EnterFunc(char* name){ if(name==NULL) return; char *temp=NULL; int len=strlen(name); if(name!=NULL){ temp=(char*)malloc(len+1); if(temp==NULL) return; memcpy(temp,name,len+1); } FunctionStack.InsertAfter(temp); if(OutputType==0){ fprintf(LogFileHandle,"\nFunction %s entered.\n",name); fflush(LogFileHandle); return; } if(OutputType==1){ (*LogStream)<<"\nFunction "<flush(); return; } } /*LeaveFunc Tells the debug lib you have left a function. Used to help see where errors have occured. Takes the last function you told it you entered off the stack. */ void LeaveFunc(){ char *name; FunctionStack.Get(name); FunctionStack.Remove(); if(OutputType==0){ fprintf(LogFileHandle,"Function %s left.\n",name); fflush(LogFileHandle); if(name!=NULL) free(name); //free previously memcopyed data return; } if(OutputType==1){ (*LogStream)<<"Function "<flush(); if(name!=NULL) free(name); return; } } void WrapperEnterFunc(char *func){} void WrapperLeaveFunc(){} /*NoteEvent Notes an event in the debug log. */ void NoteEvent(char* str){ if(OutputType==0){ fprintf(LogFileHandle,"%s\n",str); fflush(LogFileHandle); return; } if(OutputType==1){ (*LogStream)<flush(); return; } } /*LibMalloc Allocates memory for a pointer. Adds the newly allocated pointer to our linked list of pointer. Notes it in the log file. Updates statistics if it is a named ptr. Form 1: takes a VOIDPTR as an argument, returns an error msg Form 2: returns a void*, no error msg Returns 1 on success, 0 if it is unable to allocate the data */ int LibMalloc(unsigned int amt,VOIDPTR data,char* name){ data=malloc(amt); int x; if(data==NULL){ //error in allocation if(OutputType==0){ fprintf(LogFileHandle,"Unable to allocate %d bytes.\n",amt); fflush(LogFileHandle); return 0; } if(OutputType==1){ (*LogStream)<<"Unable to allocate "<flush(); return 0; } } numAllocations++; //update total stats totalAllocation+=amt; if(name!=NULL){ //pointer is named NamedStats NamedData(name); for(x=0;xflush(); } } NamedData.Alloc(amt); //update stats NamedPtrData.Remove(x); NamedPtrData.InsertBefore(NamedData); } } PtrData insertPtr(data,amt,name); //add to big list of pointers AllocatedPtrs.InsertBefore(insertPtr); if(OutputType==0){ fprintf(LogFileHandle,"Pointer %s allocated. Size %d bytes.\n",name!=NULL?name:"",amt); fflush(LogFileHandle); return 1; } if(OutputType==1){ (*LogStream)<<"Pointer "<<(name!=NULL?name:"")<<" allocated. Size "<flush(); return 1; } return 1; } void* LibMalloc(unsigned int amt,char* name){ void *data=malloc(amt); int x; if(data==NULL){ //error in allocation if(OutputType==0){ fprintf(LogFileHandle,"Unable to allocate %d bytes.\n",amt); fflush(LogFileHandle); return data; } if(OutputType==1){ (*LogStream)<<"Unable to allocate "<flush(); return data; } } numAllocations++; //update total stats totalAllocation+=amt; if(name!=NULL){ //pointer is named NamedStats NamedData(name); for(x=0;xflush(); } } NamedData.Alloc(amt); //update stats NamedPtrData.Remove(x); NamedPtrData.InsertBefore(NamedData); } } PtrData insertPtr(data,amt,name); //add to big list of pointers AllocatedPtrs.InsertBefore(insertPtr); if(OutputType==0){ fprintf(LogFileHandle,"Pointer %s allocated. Size %d bytes.\n",name!=NULL?name:"",amt); fflush(LogFileHandle); return data; } if(OutputType==1){ (*LogStream)<<"Pointer "<<(name!=NULL?name:"")<<" allocated. Size "<flush(); return data; } return data; } /*LibFree Frees a pointer allocated by LibMalloc. Traps errors and updates all stats. Returns 1=success 0=NULL ptr passed -1=freed an unallocated ptr */ int LibFree(void* data,char* name){ int x; PtrData temp(NULL,0,NULL); if(data==NULL){ //free a null if(OutputType==0){ fprintf(LogFileHandle,"Null pointer can't be freed\n"); fflush(LogFileHandle); return 0; } if(OutputType==1){ (*LogStream)<<"Null pointer can't be freed\n"; LogStream->flush(); return 0; } } for(x=0;xflush(); return -1; } } free(data); numDeallocations++; AllocatedPtrs.Remove(x); if(name!=NULL){ //named pointer NamedStats tempStats(NULL); for(x=0;xflush(); } } else{ //dealloc data tempStats.Dealloc(); NamedPtrData.Remove(x); NamedPtrData.InsertBefore(tempStats); } } if(OutputType==0){ fprintf(LogFileHandle,"Ptr %s freed.\n",name!=NULL?name:""); fflush(LogFileHandle); } if(OutputType==1){ (*LogStream)<<"Ptr "<<(name!=NULL?name:"")<<" freed.\n"; LogStream->flush(); } return 1; } /*FreeAll frees all dynamic memory used by the program. Does NOT free memory used internally by thsi library */ void FreeAll(){ AllocatedPtrs.First(); int len=AllocatedPtrs.Size(); for(int x=0;xflush(); } } } template void LibDelete(T* ptr,char* name){ unsigned int x; PtrData temp; if(ptr==NULL){ //free a null if(OutputType==0){ fprintf(LogFileHandle,"Null pointer can't be freed\n"); fflush(LogFileHandle); return; } if(OutputType==1){ (*LogStream)<<"Null pointer can't be freed\n"; LogStream->flush(); return; } } for(x=0;x< (unsigned int)AllocatedPtrs.Size();x++){ //get ptr data from list AllocatedPtrs.Get(temp,x); if(temp.GetData()==ptr) break; } if(x==(unsigned int)AllocatedPtrs.Size()){ //not in list, error if(OutputType==0){ fprintf(LogFileHandle,"Tried to free a non-malloced ptr %s.\n",name!=NULL?name:""); fflush(LogFileHandle); return; } if(OutputType==1){ (*LogStream)<<"Tried to free a non-malloced ptr "<<(name!=NULL?name:"")<<".\n"; LogStream->flush(); return; } } numDeallocations++; delete []ptr; AllocatedPtrs.Remove(x); if(name!=NULL){ //named pointer NamedStats tempStats(NULL); for(x=0;x<(unsigned int)NamedPtrData.Size();x++){ //find data with that name NamedPtrData.Get(tempStats,x); if(stricmp(tempStats.GetName(),name)==0) break; } if(x==(unsigned int)NamedPtrData.Size()){ //no ptr withb that name. Programmer typo or mismatch if(OutputType==0){ fprintf(LogFileHandle,"Ptr name %s was never allocated.\n",name!=NULL?name:""); fflush(LogFileHandle); } if(OutputType==1){ (*LogStream)<<"Ptr name "<<(name!=NULL?name:"")<<" was never allocated.\n"; LogStream->flush(); } } else{ //dealloc data tempStats.Dealloc(); NamedPtrData.Remove(x); NamedPtrData.InsertBefore(tempStats); } } if(OutputType==0){ fprintf(LogFileHandle,"Ptr %s freed.\n",name!=NULL?name:""); fflush(LogFileHandle); } if(OutputType==1){ (*LogStream)<<"Ptr "<<(name!=NULL?name:"")<<" freed.\n"; LogStream->flush(); } return; } /*WrapperDelete wrapper for delete. */ template void WrapperDelete(T* ptr,char* name){ delete []ptr; } /*LibNew Allocates memory like a new function does. Calls the constructor like new does. data is passed only so the template can figure out what T is. */ template T* LibNew (unsigned int num,T* junk,char* name){ int x; T* data=new T[num]; if(data==NULL){ //error in allocation if(OutputType==0){ fprintf(LogFileHandle,"Unable to allocate %d bytes.\n",num*sizeof(T)); fflush(LogFileHandle); return data; } if(OutputType==1){ (*LogStream)<<"Unable to allocate "<flush(); return data; } } numAllocations++; //update total stats totalAllocation+=num*sizeof(T); if(name!=NULL){ //pointer is named NamedStats NamedData(name); for(x=0;xflush(); } } NamedData.Alloc(num*sizeof(T)); //update stats NamedPtrData.Remove(x); NamedPtrData.InsertBefore(NamedData); } } PtrData insertPtr(data,num*sizeof(T),name); //add to big list of pointers AllocatedPtrs.InsertBefore(insertPtr); if(OutputType==0){ fprintf(LogFileHandle,"Pointer %s allocated. Size %d bytes.\n",name!=NULL?name:"",num*sizeof(T)); fflush(LogFileHandle); return data; } if(OutputType==1){ (*LogStream)<<"Pointer "<<(name!=NULL?name:"")<<" allocated. Size "<flush(); return data; } return data; } /*WrapperNew- wrapper for non debug version of New */ template T* WrapperNew (unsigned int num,T* junk,char* name){ return new T[num]; }