8e33b480ded84760b7c96de88ab953102fbff466 braney Fri Mar 15 10:56:11 2024 -0700 replace code that was potentially allocating too much memory off the stack with memory allocated off the heap and never freed. diff --git src/lib/basicBed.c src/lib/basicBed.c index 3899ebb..3251f15 100644 --- src/lib/basicBed.c +++ src/lib/basicBed.c @@ -1424,30 +1424,49 @@ boolean result = FALSE; struct asObject *asStandard = NULL; if (numColumnsToCheck > 15) errAbort("There are only 15 standard BED columns defined and you have asked for %d.", numColumnsToCheck); if (numColumnsToCheck < 3) errAbort("All BED files must have at least 3 columns. (Is it possible that you provided a chrom.sizes file instead of a BED file?)"); char *asStandardText = bedAsDef(15,15); asStandard = asParseText(asStandardText); result = asCompareObjs("Yours", asYours, "BED Standard", asStandard, numColumnsToCheck, NULL, abortOnDifference); freeMem(asStandardText); asObjectFreeList(&asStandard); return result; } +static char *getScratchMem(unsigned size) +// allocate some temporary memory that will never be freed except +// if it's not big enough. +{ +static unsigned currentSize = 0; +static char *currentMem = NULL; + +if (size > currentSize) + { + if (currentMem) + freeMem(currentMem); + + currentSize = size; + currentMem = needLargeMem(size); + } + +return currentMem; +} + void loadAndValidateBedExt(char *row[], int bedFieldCount, int fieldCount, struct lineFile *lf, struct bed * bed, struct asObject *as, boolean isCt, boolean allow1bpOverlap) /* Convert a row of strings to a bed and validate the contents. Abort with message if invalid data. Optionally validate bedPlus via asObject. * If a customTrack, then some errors are tolerated. Possibly allow exons to overlap by one base. */ { int count; int *blockSizes = NULL; int *chromStarts; bed->chrom = row[0]; // note this value is not cloned for speed, callers may need to clone it. // This check is usually redundant since the caller should be checking it against actual chromInfo names // however hgLoadBed might not always have that info available. if (strlen(bed->chrom) >= BB_MAX_CHROM_STRING) // must leave room for 0 terminator lineFileAbort(lf, "chrom [%s] is too long (must not exceed %d characters)", bed->chrom, BB_MAX_CHROM_STRING - 1); if (strlen(bed->chrom) < 1) @@ -1533,34 +1552,40 @@ } else { lineFileAllInts(lf, row, 8, &bed->itemRgb, FALSE, 4, "integer", FALSE); } } int tempArraySize = 1; // How big arrays are below if (bedFieldCount > 9) { lineFileAllInts(lf, row, 9, &bed->blockCount, FALSE, 4, "integer", FALSE); if (!(bed->blockCount >= 1)) lineFileAbort(lf, "Expecting blockCount (%d) to be 1 or more.", bed->blockCount); tempArraySize = bed->blockCount; } -int tempBlockSizes[tempArraySize]; -int tempChromStarts[tempArraySize]; -int tempExpIds[tempArraySize]; -float tempExpScores[tempArraySize]; + +// this memory never gets freed, but tempArraySize can be huge so +// we can't allocate it off the stack and we don't want to be +// allocating and freeing it millions of times on huge beds. +char *allocatedMem = getScratchMem(3 * tempArraySize * sizeof(int) + tempArraySize * sizeof(float)); +int *tempBlockSizes = (int *)allocatedMem; +int *tempChromStarts = (int *)&allocatedMem[tempArraySize * sizeof(int)]; +int *tempExpIds = (int *)&allocatedMem[2*tempArraySize * sizeof(int)]; +float *tempExpScores = (float *)&allocatedMem[3*tempArraySize * sizeof(int)]; + if (bedFieldCount > 10) { if (isCt) { AllocArray(bed->blockSizes,bed->blockCount+1); // having +1 allows us to detect incorrect size count = lineFileAllIntsArray(lf, row, 10, bed->blockSizes, bed->blockCount+1, TRUE, 4, "integer", TRUE); blockSizes = bed->blockSizes; } else { count = lineFileAllIntsArray(lf, row, 10, tempBlockSizes, tempArraySize, TRUE, 4, "integer", TRUE); blockSizes = tempBlockSizes; } if (count != bed->blockCount) lineFileAbort(lf, "Expecting %d elements in blockSizes list, found at least %d", bed->blockCount, count);