Sign in to follow this  
EnigmaticCoder

Memory Leaks Galore -- Dreamcast Tile Loader

Recommended Posts

I've been working on a tile loader over that past few days, which seemed to work, until I ran the program on my Dreamcast. On Windows, I did a few tests and printed out the parsed data from my loaded map file, which lead me to believe the program was in working order. However, after spending most of the day trying to figure out why the program wouldn't work on the Dreamcast, I decided to go back to Windows and use Visual Studio's built-in memory leak detector, on a hunch. I had tons of leaks, usually multiple leaks on the same line. I've been looking through the code, but the problem is not jumping out at me. I have to admit that I usually don't allocate data on the heap, so this is kind of new ground for me. Will someone look through my code and help me sort out my problems? Here is the file where the memory leaks occur:
#include "ReadMapData.h"

void getMapData(const char *fileName, Map *map)
{
	char *str = convertToCharAr(fileName);
	stripCharacter(str, ' ');
	stripCharacter(str, '\t');
	stripCharacter(str, '\n');
	allocateData(map);
	readHeader(str, map);
	readMap(str, map);
	cleanUp(str);
}

void allocateData(Map *map)
{
	const short INITIAL_SIZE = 500;
	const short SMALL_INITIAL_SIZE = 5;
	int i = 0;
	map->delimeter = malloc(SMALL_INITIAL_SIZE * sizeof(char));
	if (map->delimeter == NULL)
		exit(1);

	map->filePath = malloc(INITIAL_SIZE * sizeof(char));
	if (map->filePath == NULL)
		exit(1);

	map->mapName = malloc(INITIAL_SIZE * sizeof(char));
	if (map->mapName == NULL)
		exit(1);

	map->newLine = malloc(SMALL_INITIAL_SIZE * sizeof(char));
	if (map->newLine == NULL)
		exit(1);

	map->tileAr = malloc(INITIAL_SIZE * sizeof(short *));
	if (map->tileAr == NULL)
		exit(1);
	for (i = 0; i < INITIAL_SIZE; ++i)
	{
		map->tileAr[i] = malloc(INITIAL_SIZE * sizeof(short));
		if (map->tileAr[i] == NULL)
			exit(1);
	}

	map->tileIndexes = malloc(INITIAL_SIZE * sizeof(short *));
	if (map->tileIndexes == NULL)
		exit(1);
	for (i = 0; i < INITIAL_SIZE; ++i)
	{
		map->tileIndexes[i] = malloc(INITIAL_SIZE * sizeof(short));
		if (map->tileIndexes[i] == NULL)
			exit(1);
	}
}

void cleanUp(char *str)
{
	if (str == NULL)
		exit(1);
	free(str);
}

//This function converts the text file to a string so it is more easy to deal with
char *convertToCharAr(const char *fileName)
{
	const short OFFSET = 1;
	const short INITIAL_SIZE = 500;
	short strSize = INITIAL_SIZE;
	short currentIndex = 0;
	char *str = malloc(sizeof(char) * strSize);
	int ch;

	FILE *f;

	f = fopen(fileName, "r");
	if (f == NULL)
		exit(1); //Error opening file
	while((ch = fgetc(f)) != EOF)
	{
		growIfNeeded((void **)&str, sizeof(char), &strSize, currentIndex + OFFSET);
		str[currentIndex] = ch;
		++currentIndex;
	}
	fclose(f);

	//Add the null terminating character
	growIfNeeded((void **)&str, sizeof(char), &strSize, currentIndex + OFFSET);
	str[currentIndex] = '\0';
	return str;
}

void growIfNeeded(void **data, short pointerSize, short *curSize, short neededSize)
{	
	const short GROW_BY = 100;

	if (neededSize >= *curSize)
	{
		*curSize += GROW_BY;
		if (pointerSize == sizeof(char))
		{
			void *tmp = realloc(*data, *curSize * sizeof(char));
			if (tmp == NULL)
				exit(1);
			*data = tmp;
		}
		else if (pointerSize == sizeof(short))
		{
			void *tmp = realloc(*data, *curSize * sizeof(short));
			if (tmp == NULL)
				exit(1);
			*data = tmp;
		}
		else if (pointerSize == sizeof(int))
		{
			void *tmp = realloc(*data, *curSize * sizeof(int));
			if (tmp == NULL)
				exit(1);
			*data = tmp;
		}
	}
}

void growIfNeeded2D(void ***data, short arrayType, short *curRows, short curCols, short neededSize)
{
	const short OFFSET = 1;
	const short GROW_BY = 100;
	short lastSize = *curRows;
	short i = 0;

	if (neededSize >= *curRows)
	{
		*curRows += GROW_BY;
		if (arrayType == sizeof(char))
		{
			void **tmp = realloc(*data, *curRows * sizeof(char *));
			if (tmp == NULL)
				exit(1);
			*data = tmp;
			
			//Must allocate new memory because the array grew
			for (i = lastSize - OFFSET; i < *curRows - OFFSET; ++i)
			{
				*data[i] = malloc(curCols * sizeof(char));
				if (*data[i] == NULL)
					exit(1);
			}
		}
		else if (arrayType == sizeof(short))
		{
			void **tmp = realloc(*data, *curRows * sizeof(short *));
			if (tmp == NULL)
				exit(1);

			//Must allocate new memory because the array grew
			for (i = lastSize - OFFSET; i < *curRows - OFFSET; ++i)
			{
				tmp[i] = malloc(curCols * sizeof(int));
				if (tmp[i] == NULL)
					exit(1);
			}
			*data = tmp;
		}
		else if (arrayType == sizeof(int))
		{
			void **tmp = realloc(*data, *curRows * sizeof(int *));
			if (tmp == NULL)
				exit(1);

			//Must allocate new memory because the array grew
			for (i = lastSize - OFFSET; i < *curRows - OFFSET; ++i)
			{
				tmp[i] = malloc(curCols * sizeof(int));
				if (tmp[i] == NULL)
					exit(1);
			}
			*data = tmp;
		}
	}
}

void readHeader(char *str, Map *map)
{
	const char *HEADER = "HEADER";
	const char *END_HEADER = "END_HEADER";
	const char *MAP_NAME = "MAP_NAME=";
	const char *FILE_PATH = "FILE_PATH=";
	const char *DELIMETER = "DELIMETER=";
	const char *NEW_LINE = "NEW_LINE=";
	const char *TILE_SIZE = "TILE_SIZE=";
	const char *TILE_INDEXES = "TILE_INDEXES=";
	short indexOfHeader = 0;
	short indexOfEndHeader = 0;
	short i = 0;

	searchFor(str, 0, HEADER, &indexOfHeader);
	indexOfHeader += getStringLength(HEADER);

	searchFor(str, indexOfHeader, END_HEADER, &indexOfEndHeader);

	i = indexOfHeader;
	strcpy(map->mapName, readDataValue(str, &i, MAP_NAME));
	strcpy(map->filePath, readDataValue(str, &i, FILE_PATH));
	strcpy(map->delimeter, readDataValue(str, &i, DELIMETER));
	strcpy(map->newLine, readDataValue(str, &i, NEW_LINE));
	map->tileSize = atoi(readDataValue(str, &i, TILE_SIZE));
	map->tileIndexes = readGridValue(str, &i, TILE_INDEXES, map->delimeter, map->newLine, &map->sheetRows, &map->sheetCols);
	i = indexOfEndHeader;
}

void readMap(char *str, Map *map)
{
	const char *MAP = "MAP";
	const char *END_HEADER = "END_HEADER";
	const char *END_MAP = "END_MAP";
	const char *MAP_SHEET = "MAP_SHEET=";
	short indexOfEndHeader = 0;
	short indexOfMap = 0;
	short indexOfEndMap = 0;
	short i = 0;

	searchFor(str, 0, END_HEADER, &indexOfEndHeader);
	indexOfEndHeader += getStringLength(END_HEADER);

	searchFor(str, indexOfEndHeader, MAP, &indexOfMap) ;
	indexOfMap += getStringLength(MAP);

	searchFor(str, indexOfMap, END_MAP, &indexOfEndMap);

	i = indexOfMap;
	map->tileAr = readGridValue(str, &i, MAP_SHEET, map->delimeter, map->newLine, &map->rows, &map->cols);
	i = indexOfEndMap;
}

char *readDataValue(char *str, short *currentIndex, const char *prependString) 
{
	const short OFFSET = 1;
	const char *END_DATA = "END_DATA";
	const short INITIAL_SIZE = 3;
	short strSize = INITIAL_SIZE;
	char *dataString = realloc(NULL, sizeof(char) * strSize);
	short indexOfData = 0;
	short indexOfEndData = 0;

	searchFor(str, *currentIndex, prependString, &indexOfData);
	indexOfData += getStringLength(prependString);

	searchFor(str, indexOfData, END_DATA, &indexOfEndData);

	for (*currentIndex = indexOfData; *currentIndex < indexOfEndData; ++*currentIndex)
	{
		growIfNeeded((void **)&dataString, sizeof(char), &strSize, *currentIndex - indexOfData  + OFFSET);
		dataString[*currentIndex - indexOfData] = str[*currentIndex];
	}
	growIfNeeded((void **)&dataString, sizeof(char), &strSize, *currentIndex - indexOfData  + OFFSET);
	dataString[*currentIndex - indexOfData] = '\0';

	return  dataString;
}

short **readGridValue(char *str, short *currentIndex, const char* prependString,  
      char *delimeter, char *newLineDelimeter, short *rows, short *cols)
{
	const short INITIAL_SIZE = 500;
	const char *END_GRID = "END_GRID";
	const short INITIAL_ROWS = 100;
	const short INITIAL_COLS = 100;
	char element[16];
	short neededCols = 0;
	short neededRows = 0;
	short curRows = INITIAL_ROWS;
	short curCols = INITIAL_COLS;
	short **tiles;
	short indexOfGrid = 0;
	short indexOfEndGrid = 0;
	short i = 0;

	tiles = malloc(INITIAL_SIZE * sizeof(short *));
	if (tiles == NULL)
		exit(1);
	for (i = 0; i < INITIAL_SIZE; ++i)
	{
		tiles[i] = malloc(INITIAL_SIZE * sizeof(short));
		if (tiles[i] == NULL)
			exit(1);
	}

	searchFor(str, *currentIndex, prependString, &indexOfGrid);
	indexOfGrid += getStringLength(prependString);

	searchFor(str, indexOfGrid, END_GRID, &indexOfEndGrid);

	*currentIndex = indexOfGrid;
	while (*currentIndex < indexOfEndGrid)
	{
		neededCols = 0;
		growIfNeeded2D((void***)&tiles, sizeof(short), &curRows, curCols, neededRows);

		while (strncmp(&str[*currentIndex], newLineDelimeter, getStringLength(newLineDelimeter)))
		{
			growIfNeeded((void **)&tiles[neededRows], sizeof(short), &curCols, neededCols);
			populateElement(element, str, *currentIndex, delimeter);
			tiles[neededRows][neededCols] = atoi(element);
			++neededCols;
			*currentIndex += getStringLength(element) + getStringLength(delimeter);
		}
		*currentIndex += getStringLength(newLineDelimeter);
		++neededRows;
	}
	tiles = realloc(tiles, (neededRows) * sizeof(short *)); //numOfRows - 1 because there was an extra increment at the end of the loop
	if (tiles == NULL)
		exit(1);
	for (i = 0; i < neededRows; ++i)
	{
		tiles[i] = realloc(tiles[i], (neededCols) * sizeof(short)); //numOfCols - 1 because there was an extra increment at the end of the loop
		if (tiles[i] == NULL)
			exit(1);
	}

	*rows = neededRows;
	*cols = neededCols;

	return tiles;
}

void populateElement(char *element, char *str, short currentIndex, char *delimeter)
{
	short i = 0;
	short stop = 0;
	searchFor(str, currentIndex, delimeter, &stop);

	for (i = 0;currentIndex < stop; ++currentIndex, ++i)
	{
		if (str[currentIndex])
			element[i] = str[currentIndex];
	}
	element[i] = '\0';
}

short getStringLength(const char *str)
{
	short size = 0;

	while(str[size])
		++size;

	return size;
}

void searchFor(char *str, short startIndex, const char *toFind, short *foundAt)
{
	const short OFFSET = 1;
	short strLength = getStringLength(str);
	short toFindLength = getStringLength(toFind);
	//bool found = false;

	while (startIndex + toFindLength - OFFSET < strLength) //&& !found
	{
		if (!strncmp(&str[startIndex], toFind, toFindLength))
		{
			*foundAt = startIndex;
			//found = true;
			break;
		}
		++startIndex;
	}

	//return found;
}

void stripCharacter(char *str, char toStrip)
{
	char *write = str;
	char *read  = str;

	//Strip the white spaces out by 'skipping over' each space character and overwriting the string
	while(*read)
	{
		if (*read == toStrip) 
			++read;
		else 
			*write++ = *read++;
	}

	//Add the null character because it was left off
	*write = '\0'; 
}








The lines with memory leaks are: 283 241 102 51 46 41 36 And if you're interested in what a sample map looks like: http://anothrguitarist.googlepages.com/sampleMap.txt Any help would be greatly appreciated. EDIT: I forgot to mention that I free the memory in another file. See below. [Edited by - anothrguitarist on August 17, 2008 3:22:36 PM]

Share this post


Link to post
Share on other sites
You're mallocing and reallocing to your hearts desire, without ever freeing all that memory. Whatever memory you allocate on the heap must be freed manually once you're done with it.

Share this post


Link to post
Share on other sites
Whoops, I forgot to mention that I have a function to free that memory in a separate file. Here it is:


#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#include "ReadMapData.h"
#include "Map.h"


void freeMapData(Map *map);

int main()
{
int i = 0;
int j = 0;
Map map;
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
getMapData("sampleMap.txt", &map);
for (i = 0; i < map.sheetRows; ++i)
{
for (j = 0; j < map.sheetCols; ++j)
{
if (j > 9 && i > 9 && map.tileIndexes[i][i] > 9)
printf("Map[%d][%d] =%d\t", i, j, map.tileIndexes[i][j]);
else
printf("Map[%d][%d] = %d\t", i, j, map.tileIndexes[i][j]);
}
}
printf("MAP_NAME = %s\n", map.mapName);
printf("FILE_PATH = %s\n", map.filePath);
printf("TILE_SIZE = %d\n", map.tileSize);
freeMapData(&map);
return 0;
}

void freeMapData(Map *map)
{
int i = 0;
if (map->mapName == NULL)
exit(1);
free(map->mapName);

if (map->filePath == NULL)
exit(1);
free(map->filePath);

if (map->delimeter == NULL)
exit(1);
free(map->delimeter);

if (map->newLine == NULL)
exit(1);
free(map->newLine);

for (i = 0; i < map->rows; ++i)
{
if (map->tileAr[i] == NULL)
exit(1);
free(map->tileAr[i]);
}

if (map->tileAr == NULL)
exit(1);
free(map->tileAr);

for (i = 0; i < map->sheetRows; ++i)
{
if (map->tileIndexes[i] == NULL)
exit(1);
free(map->tileIndexes[i]);
}

if (map->tileIndexes == NULL)
exit(1);
free(map->tileIndexes);
}




Just to note, I plan on refactoring so I avoid reallocating data so often. What I'm going to do instead is a two pass read of the file. In the first pass, the program is going to find how much data should be allocated, and in the second pass, it will actually store the data. What I want to do now, however, is figure out how to get this preliminary version working, so I can move on.

[Edited by - anothrguitarist on August 17, 2008 3:48:29 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this