diff options
Diffstat (limited to '3dc/mem3dc.c')
| -rw-r--r-- | 3dc/mem3dc.c | 824 |
1 files changed, 0 insertions, 824 deletions
diff --git a/3dc/mem3dc.c b/3dc/mem3dc.c deleted file mode 100644 index d0da027..0000000 --- a/3dc/mem3dc.c +++ /dev/null @@ -1,824 +0,0 @@ - -/******************************************************************* - * - * DESCRIPTION: mem3dc.c - * - * AUTHOR: Rob Rodger - * - * HISTORY: 23/12/96 - * - *******************************************************************/ - - -/* mem3dc.c simply keeps a record of all memory allocations called by */ -/* AllocateMem() and checks if DeallocateMem() calls are legitimate */ -/* i.e. the address freed has been allocated. This record */ -/* includes the address, size, filename and line number for the associated */ -/* AllocateMem() call. */ -/* */ -/* Also, fills in MALLOC_FILL_VALUE in memory malloced and FREE_FILL_VALUE */ -/* in memory freed. */ -/* */ -/* A record is kept of the total amount of allocated memory outstanding in */ -/* global TotalMemAllocated as well as the total number of outstanding mallocs */ -/* in the global TotalMallocNum. Finally, a call to DumpMallocInfo(DUMPTOSCREEN) */ -/* will give a textprint of each outstanding malloc record while */ -/* while DumpMallocInfo(DUMPTOFILE) writes malloc records to a file with */ -/* filename defined by MALLOCDUMPFILE define. Set APPEND_TO_DUMPFILE to 1 */ -/* in define below if you wish to append malloc info to MALLOCDUMPFILE rather */ -/* than over writing file each time. */ -/* */ -/* To use, define DBGMALLOC as 1 in "mem3dc.h". Obviously for final code */ -/* set DBGMALLOC to 0. */ -/* */ -/* Note, must use AllocateMem() and DeallocateMem() in code - Do not */ -/* make direct calls to AllocMem()/DeallocMem() contained in platform */ -/* file our_mem.c */ - -#include "3dc.h" - -#include <string.h> -#include "mem3dc.h" /* contains extern declarations for platform - specific memory allocation/deallocation. - Also contains DBGMALLOC define */ - -#if DBGMALLOC - -#include "ourasert.h" - -#if PSX || Saturn - #define MAXMALLOCS 600 -#else - #define MAXMALLOCS 2500000 - /* assertion fires if max exceeded */ - /* changed to 1000000 by DHM 7/4/98; was 70001 */ -#endif - -#define MALLOC_FILL_VALUE 0x21 -#define FREE_FILL_VALUE 0x89 -#define MALLOCDUMPFILE "dbgdump.txt" -#define APPEND_TO_DUMPFILE 0 /* define as 0 if overwrite dbgdump.txt rather than append */ -#define FREEING_MEMORY -1 - -#define AllowedToDeleteNULL Yes - /* - Option added 7/4/98 by DHM: - --------------------------- - This option checks for NULL in record_free() and doesn't update the records. - - According to 2nd and 3rd edition Stroustrup, it's legal to "delete NULL", and it - has no effect (see e.g. 3rd Edition Stroustrup, p128 paragraph 2). - According to Appendix B of K&R, free NULL has no effect. - */ - -#if APPEND_TO_DUMPFILE -#define FILEPERM "a" -#else -#define FILEPERM "w" -#endif - -#if SupportWindows95 && !defined(_MSC_VER) /* not required for MS C since MS C has CRT debugging available */ -#define OVERRUN_SIZEMIN 2 -#define OVERRUN_SIZEMAX 128 -#define OVERRUN_SIZEFACTOR 2 /* this is a shift down */ - -#define OVERRUN_SIZE(sz) ((sz)>OVERRUN_SIZEMAX<<OVERRUN_SIZEFACTOR ? OVERRUN_SIZEMAX : (sz)<OVERRUN_SIZEMIN<<OVERRUN_SIZEFACTOR ? OVERRUN_SIZEMIN : (sz)>>OVERRUN_SIZEFACTOR) -/* I just selected these at random - the 0 term is necessary for wrap around */ -static unsigned char const overrun_code [] = { 0xef, 0x94, 0x56, 0x27, 0xf6, 0x76, 0x23, 0x43, 0 }; - -#else -#undef OVERRUN_SIZE -#endif - - - -typedef struct{ - unsigned long addr; - unsigned long size; - #if COPY_FILENAME - char filename[40]; - #else - char const * filename; /* JH 30/5/97 - since __FILE__ generates a string in the executable, to which we get passed a pointer, we don't need to make another copy unnecessarily wasting 720K of space on PC !! */ - #endif - unsigned long linenum; -}MALLOC_RECORD; - -/* globals */ -MALLOC_RECORD MallocRecord[MAXMALLOCS]; -unsigned long TotalMemAllocated = 0; -unsigned long TotalMallocNum = 0; - -/* extern function declarations */ -extern int textprint(const char* string, ...); -extern void ExitSystem(void); -extern void WaitForReturn(void); - -/* textprint on psx impacts frame rate massively if not -used just for error messages, so it is often turned off. -Hence the use of textprint2 below */ - -#if PSX -#define textprint2 printf -#else -#define textprint2 textprint -#endif - -/* function declarations */ -#if COPY_FILENAME -void record_free(void *ptr, char string[], unsigned long lineno); -void *record_malloc(long size, char string[], unsigned long lineno); -static int AdjustMallocRecord(unsigned long addr, long size, char string[], unsigned long lineno); -#else /* new prototypes to take just pointers - dunno if it's really necessary */ -void record_free(void *ptr, char const * string, unsigned long lineno); -void *record_malloc(long size, char const * string, unsigned long lineno); -static int AdjustMallocRecord(unsigned long addr, long size, char const * string, unsigned long lineno); -#endif -void DumpMallocInfo(int type); -static void InitMallocRecords(void); - -/* function definitions */ - -#if COPY_FILENAME -void *record_malloc(long size, char string[], unsigned long lineno) -#else -void *record_malloc(long size, char const * string, unsigned long lineno) -#endif -{ - #ifdef OVERRUN_SIZE - void *ptr = (void *)AllocMem((size_t) size + OVERRUN_SIZE(size)); - #else - void *ptr = (void *)AllocMem((size_t) size); - #endif - if(ptr==NULL) - { - textprint2("\nMalloc Error! %d bytes attempted\n", size); - return((void *)NULL); - } - - GLOBALASSERT(size>0); /* should be redundant cos picked up by above malloc attempt */ - - AdjustMallocRecord((long)ptr,size, string, lineno); - - return(ptr); -} - -#if COPY_FILENAME -void record_free(void *ptr, char string[], unsigned long lineno) -#else -void record_free(void *ptr, char const * string, unsigned long lineno) -#endif -{ - if(AdjustMallocRecord((long)ptr,FREEING_MEMORY, string, lineno)) - DeallocMem((void *)ptr); /* free previously malloced ptr */ - return; -} - -#if PSX || Saturn - -#if COPY_FILENAME -static int AdjustMallocRecord(unsigned long addr, long size, char string[], unsigned long lineno) -#else -static int AdjustMallocRecord(unsigned long addr, long size, char const * string, unsigned long lineno) -#endif - -{ - - int i=0; - char *ptr = (char *)addr; - static int no_record_init = 1; - MALLOC_RECORD *recordPtr = MallocRecord; - - if(no_record_init) - { - InitMallocRecords(); - no_record_init = 0; - } - - - if(size==FREEING_MEMORY) /* must be freeing memory */ - { - GLOBALASSERT(addr); /* ensure not null addr */ - - while(i<MAXMALLOCS) - { - if(recordPtr->addr==addr) - { - TotalMallocNum--; - - size = recordPtr->size; - - while (size--) - { - *ptr++ = FREE_FILL_VALUE; - } - #ifdef OVERRUN_SIZE - { - char const * overrun_ptr = overrun_code; - int ov_cnt = OVERRUN_SIZE(recordPtr->size); - do /* a memcmp - dunno if its supported on all platforms */ - { - if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */ - if (*overrun_ptr++ != *ptr++) - { - textprint2("\nOut of Bounds!\n%lu bytes allocated to %p\nat %s, line %lu\n", recordPtr->size,(void *)recordPtr->addr,recordPtr->filename,recordPtr->linenum); - GLOBALASSERT(!"OUT OF BOUNDS detected in FREE"); - } - } - while (--ov_cnt); - } - #endif - - recordPtr->addr = 0; - TotalMemAllocated -= recordPtr->size; - recordPtr->size = 0; - #if COPY_FILENAME - recordPtr->filename[0] = 0; - #else - recordPtr->filename = ""; - #endif - recordPtr->linenum = 0; - break; /* exit while loop */ - } - i++; - recordPtr++; - } - - if(i>=MAXMALLOCS) - { - textprint2("\n\n\n\nFree Error! %s, line %d\n", string, (int)lineno); - GLOBALASSERT(0); - return(0); - } - - } - - else /* must be mallocing memory */ - { - TotalMallocNum++; - GLOBALASSERT(TotalMallocNum<MAXMALLOCS); /* just increase MAXMALLOCS define above if this fires */ - - - // RWH chack to see that this address isn't already in use - i = 0; - recordPtr = MallocRecord; - while(i<MAXMALLOCS) - { - if(recordPtr->addr == addr) - { - GLOBALASSERT(0); - } - i++; - recordPtr++; - } - i = 0; - recordPtr = MallocRecord; - while(i<MAXMALLOCS) - { - if(recordPtr->addr==0) - { - recordPtr->addr = addr; - recordPtr->size = size; - TotalMemAllocated += size; - #if COPY_FILENAME - strcpy(recordPtr->filename, string); - #else - recordPtr->filename = string; - #endif - recordPtr->linenum = lineno; - while (size--) - { - *ptr++ = MALLOC_FILL_VALUE; - } - #ifdef OVERRUN_SIZE - { - char const * overrun_ptr = overrun_code; - int ov_cnt = OVERRUN_SIZE(recordPtr->size); - do /* a memcpy */ - { - if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */ - *ptr++ = *overrun_ptr++; - } - while (--ov_cnt); - } - #endif - break; /* exit while loop */ - } - i++; - recordPtr++; - } - - GLOBALASSERT(i<MAXMALLOCS); /* This should never fire - oh well */ - - } - - return(1); - -} -#else -#if COPY_FILENAME -static int AdjustMallocRecord(unsigned long addr, long size, char string[], unsigned long lineno) -#else -static int AdjustMallocRecord(unsigned long addr, long size, char const * string, unsigned long lineno) -#endif -{ - - int Starti=addr % MAXMALLOCS; - int i=Starti; - char *ptr = (char *)addr; - static int no_record_init = 1; - MALLOC_RECORD *recordPtr,*StartRecordPtr; - recordPtr=StartRecordPtr=&MallocRecord[Starti]; - - if(no_record_init) - { - InitMallocRecords(); - no_record_init = 0; - } - - - if(size==FREEING_MEMORY) /* must be freeing memory */ - { - #if AllowedToDeleteNULL - if (NULL == addr) - { - return 1; - /* - ...so the record_free() function calls - DeallocMem(NULL), which ought to do nothing - (refer to citations in comment near the top of - this file) - */ - } - #endif - - GLOBALASSERT(addr); /* ensure not null addr */ - - while(i<MAXMALLOCS) - { - if(recordPtr->addr==addr) - { - TotalMallocNum--; - - size = recordPtr->size; - - while (size--) - { - *ptr++ = FREE_FILL_VALUE; - } - #ifdef OVERRUN_SIZE - { - char const * overrun_ptr = overrun_code; - int ov_cnt = OVERRUN_SIZE(recordPtr->size); - do /* a memcmp - dunno if its supported on all platforms */ - { - if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */ - if (*overrun_ptr++ != *ptr++) - { - textprint2("\nOut of Bounds!\n%lu bytes allocated to %p\nat %s, line %lu\n", recordPtr->size,(void *)recordPtr->addr,recordPtr->filename,recordPtr->linenum); - GLOBALASSERT(!"OUT OF BOUNDS detected in FREE"); - } - } - while (--ov_cnt); - } - #endif - - recordPtr->addr = 0; - TotalMemAllocated -= recordPtr->size; - recordPtr->size = 0; - #if COPY_FILENAME - recordPtr->filename[0] = 0; - #else - recordPtr->filename = ""; - #endif - recordPtr->linenum = 0; - break; /* exit while loop */ - } - i++; - recordPtr++; - } - if(i==MAXMALLOCS) - { - i=0; - recordPtr=&MallocRecord[0]; - - while(i<Starti) - { - if(recordPtr->addr==addr) - { - TotalMallocNum--; - - size = recordPtr->size; - - while (size--) - { - *ptr++ = FREE_FILL_VALUE; - } - #ifdef OVERRUN_SIZE - { - char const * overrun_ptr = overrun_code; - int ov_cnt = OVERRUN_SIZE(recordPtr->size); - do /* a memcmp - dunno if its supported on all platforms */ - { - if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */ - if (*overrun_ptr++ != *ptr++) - { - textprint2("\nOut of Bounds!\n%lu bytes allocated to %p\nat %s, line %lu\n", recordPtr->size,(void *)recordPtr->addr,recordPtr->filename,recordPtr->linenum); - GLOBALASSERT(!"OUT OF BOUNDS detected in FREE"); - } - } - while (--ov_cnt); - } - #endif - - recordPtr->addr = 0; - TotalMemAllocated -= recordPtr->size; - recordPtr->size = 0; - #if COPY_FILENAME - recordPtr->filename[0] = 0; - #else - recordPtr->filename = ""; - #endif - recordPtr->linenum = 0; - break; /* exit while loop */ - } - i++; - recordPtr++; - } - if(i>=Starti) - { - textprint2("\n\n\n\nFree Error! %s, line %d\n", string, (int)lineno); - GLOBALASSERT(0); - return(0); - } - } - } - - else /* must be mallocing memory */ - { - TotalMallocNum++; - GLOBALASSERT(TotalMallocNum<MAXMALLOCS); /* just increase MAXMALLOCS define above if this fires */ - - - // RWH chack to see that this address isn't already in use - while(i<MAXMALLOCS) - { - if(recordPtr->addr==0) - { - recordPtr->addr = addr; - recordPtr->size = size; - TotalMemAllocated += size; - #if COPY_FILENAME - strcpy(recordPtr->filename, string); - #else - recordPtr->filename = string; - #endif - recordPtr->linenum = lineno; - while (size--) - { - *ptr++ = MALLOC_FILL_VALUE; - } - #ifdef OVERRUN_SIZE - { - char const * overrun_ptr = overrun_code; - int ov_cnt = OVERRUN_SIZE(recordPtr->size); - do /* a memcpy */ - { - if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */ - *ptr++ = *overrun_ptr++; - } - while (--ov_cnt); - } - #endif - break; /* exit while loop */ - } - i++; - recordPtr++; - } - if(i>=MAXMALLOCS) - { - i=0; - recordPtr=&MallocRecord[0]; - while(i<Starti) - { - if(recordPtr->addr==0) - { - recordPtr->addr = addr; - recordPtr->size = size; - TotalMemAllocated += size; - #if COPY_FILENAME - strcpy(recordPtr->filename, string); - #else - recordPtr->filename = string; - #endif - recordPtr->linenum = lineno; - while (size--) - { - *ptr++ = MALLOC_FILL_VALUE; - } - #ifdef OVERRUN_SIZE - { - char const * overrun_ptr = overrun_code; - int ov_cnt = OVERRUN_SIZE(recordPtr->size); - do /* a memcpy */ - { - if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */ - *ptr++ = *overrun_ptr++; - } - while (--ov_cnt); - } - #endif - break; /* exit while loop */ - } - i++; - recordPtr++; - } - GLOBALASSERT(i<Starti); /* This should never fire - oh well */ - } - } - return(1); - -} -#endif -void DumpMallocInfo(int type) -{ - int i; - - if(type==DUMPTOSCREEN) - { - for (i=0;i<MAXMALLOCS;i++ ) - { - if(MallocRecord[i].addr!=0) - { - if (!(MallocRecord[i].linenum & CPPGLOBAL)) - { - textprint2("\nmalloc: %d bytes,line %d in %s", - (int)MallocRecord[i].size, (int)MallocRecord[i].linenum, MallocRecord[i].filename); - } - else - { - textprint2("\nC++ global construct malloc: %d bytes,line %d in %s", - (int)MallocRecord[i].size, (int)(MallocRecord[i].linenum &~CPPGLOBAL), MallocRecord[i].filename); - } - } - } - textprint2("\n\n\n\nTotalMemAllocated: %d\nTotalMallocNum: %d", - (int)TotalMemAllocated, (int)TotalMallocNum); - WaitForReturn(); - } - else if (type==DUMPTOFILE) - { - - #if SupportWindows95 - FILE *fp; - - if( (fp = fopen(MALLOCDUMPFILE,FILEPERM))== (FILE *)NULL) - { - textprint2("\n\n\nfile open error %s", MALLOCDUMPFILE); - } - else - { - fprintf(fp,"\n\n\n\nOUTSTANDING MEMORY ALLOCATIONS\n\n"); - for (i=0;i<MAXMALLOCS;i++ ) - { - if(MallocRecord[i].addr!=0) - { - if (!(MallocRecord[i].linenum & CPPGLOBAL)) - { - fprintf(fp,"\nmalloc: %d bytes,line %d in %s", - (int)MallocRecord[i].size, (int)MallocRecord[i].linenum, MallocRecord[i].filename); - } - else - { - fprintf(fp,"\nC++ global construct malloc: %d bytes,line %d in %s", - (int)MallocRecord[i].size, (int)(MallocRecord[i].linenum &~CPPGLOBAL), MallocRecord[i].filename); - } - } - } - fprintf(fp,"\n\nTotalMemAllocated: %d\nTotalMallocNum: %d\n", - (int)TotalMemAllocated, (int)TotalMallocNum); - - fclose(fp); - } - #endif - - #if PSX - long fd; - - fd = PCopen(MALLOCDUMPFILE,1,0); - if(fd<0) - { - fd = PCcreat(MALLOCDUMPFILE,0); - PCclose(fd); - fd = PCopen(MALLOCDUMPFILE,1,0); - } - - if(fd<0) - { - textprint2("\n\n\nfile open error %s", MALLOCDUMPFILE); - } - else - { - char stringbuf[200]; - sprintf(stringbuf,"\n\n\n\nOUTSTANDING MEMORY ALLOCATIONS\n\n"); - #if APPEND_TO_DUMPFILE - PClseek(fd,0,2); - #endif - PCwrite(fd,stringbuf,strlen(stringbuf)); - for (i=0;i<MAXMALLOCS;i++ ) - { - if(MallocRecord[i].addr!=0) - { - if (!(MallocRecord[i].linenum & CPPGLOBAL)) - { - sprintf(stringbuf,"\nmalloc: %d bytes,line %d in %s", - (int)MallocRecord[i].size, (int)MallocRecord[i].linenum, MallocRecord[i].filename); - } - else - { - sprintf(stringbuf,"\nC++ global construct malloc: %d bytes,line %d in %s", - (int)MallocRecord[i].size, (int)(MallocRecord[i].linenum &~CPPGLOBAL), MallocRecord[i].filename); - } - PCwrite(fd,stringbuf,strlen(stringbuf)); - } - } - - sprintf(stringbuf,"\n\nTotalMemAllocated: %d\nTotalMallocNum: %d\n", - (int)TotalMemAllocated, (int)TotalMallocNum); - - PCwrite(fd,stringbuf,strlen(stringbuf)); - - PCclose(fd); - - - } - - #endif - - } - -} - -void DumpBoundsCheckInfo(int type) -{ - #ifdef OVERRUN_SIZE - int i; - - if(type==DUMPTOSCREEN) - { - int bc_errcnt = 0; - for (i=0;i<MAXMALLOCS;i++ ) - { - if(MallocRecord[i].addr!=0) - { - MALLOC_RECORD *recordPtr = &MallocRecord[i]; - - unsigned char const * overrun_ptr = overrun_code; - unsigned char const * ptr = (unsigned char const *)recordPtr->addr + recordPtr->size; - int ov_cnt = OVERRUN_SIZE(recordPtr->size); - do /* a memcmp - dunno if its supported on all platforms */ - { - if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */ - if (*overrun_ptr++ != *ptr++) - { - textprint2("\nOut of Bounds!\n%lu bytes allocated to %p\nat %s, line %lu\n", recordPtr->size,(void *)recordPtr->addr,recordPtr->filename,recordPtr->linenum); - ++bc_errcnt; - break; - } - } - while (--ov_cnt); - } - } - if (bc_errcnt) - WaitForReturn(); - else - textprint2("No bounds errors detected\n"); - - } - else if (type==DUMPTOFILE) - { - - #if SupportWindows95 - FILE *fp; - - if( (fp = fopen(MALLOCDUMPFILE,FILEPERM))== (FILE *)NULL) - { - textprint2("\n\n\nfile open error %s", MALLOCDUMPFILE); - } - else - { - fprintf(fp,"\n\n\n\nBOUNDS CHECK PROBLEMS\n\n"); - for (i=0;i<MAXMALLOCS;i++ ) - { - if(MallocRecord[i].addr!=0) - { - MALLOC_RECORD *recordPtr = &MallocRecord[i]; - - unsigned char const * overrun_ptr = overrun_code; - unsigned char const * ptr = (unsigned char const *)recordPtr->addr + recordPtr->size; - int ov_cnt = OVERRUN_SIZE(recordPtr->size); - do /* a memcmp - dunno if its supported on all platforms */ - { - if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */ - if (*overrun_ptr++ != *ptr++) - { - fprintf(fp,"\nOut of Bounds!\n%lu bytes allocated to %p\nat %s, line %lu\n", recordPtr->size,(void *)recordPtr->addr,recordPtr->filename,recordPtr->linenum); - break; - } - } - while (--ov_cnt); - } - } - fprintf(fp,"\n\nTotalMemAllocated: %d\nTotalMallocNum: %d\n", - (int)TotalMemAllocated, (int)TotalMallocNum); - - fclose(fp); - } - #endif - - #if PSX - long fd; - - fd = PCopen(MALLOCDUMPFILE,1,0); - if(fd<0) - { - fd = PCcreat(MALLOCDUMPFILE,0); - PCclose(fd); - fd = PCopen(MALLOCDUMPFILE,1,0); - } - - if(fd<0) - { - textprint2("\n\n\nfile open error %s", MALLOCDUMPFILE); - } - else - { - char stringbuf[200]; - sprintf(stringbuf,"\n\n\n\nBOUNDS_CHECK_PROBLEMS\n\n"); - #if APPEND_TO_DUMPFILE - PClseek(fd,0,2); - #endif - PCwrite(fd,stringbuf,strlen(stringbuf)); - for (i=0;i<MAXMALLOCS;i++ ) - { - if(MallocRecord[i].addr!=0) - { - MALLOC_RECORD *recordPtr = &MallocRecord[i]; - - unsigned char const * overrun_ptr = overrun_code; - unsigned char const * ptr = (unsigned char const *)recordPtr->addr + recordPtr->size; - int ov_cnt = OVERRUN_SIZE(recordPtr->size); - do /* a memcmp - dunno if its supported on all platforms */ - { - if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */ - if (*overrun_ptr++ != *ptr++) - { - sprintf(stringbuf,"\nOut of Bounds!\n%lu bytes allocated to %p\nat %s, line %lu\n", recordPtr->size,(void *)recordPtr->addr,recordPtr->filename,recordPtr->linenum); - PCwrite(fd,stringbuf,strlen(stringbuf)); - break; - } - } - while (--ov_cnt); - } - } - - PCclose(fd); - - - } - - #endif - - } - - #endif -} - -void InitMallocRecords(void) -{ - int i; - MALLOC_RECORD *recordPtr = MallocRecord; - - for (i=0;i<MAXMALLOCS;i++,recordPtr++) - { - recordPtr->addr = 0; - recordPtr->size = 0; - #if COPY_FILENAME - recordPtr->filename[0] = 0; - #else - recordPtr->filename = ""; - #endif - recordPtr->linenum = 0; - } - - return; -} - -#else -void DumpMallocInfo(int type); -void DumpMallocInfo(int type) -{ - /* empty if not debugging */ -} -void DumpBoundsCheckInfo(int type) -{} -#endif |
