what object to use to read a file in it?

Started by
3 comments, last by moucard 19 years, 10 months ago
Hi to everyone! I''d like to read a whole file into an object (the file is at most around 350KB) and then read the object instead of the file. Right now I''m reading from the file each time and it slows down my app. (I''m trying to do a replay system) I''m almost sure it''s the file access that slows it down because I''m using the same program, slightly alter where I read a certain type of event (only the mouse move) into a vector and then the replay goes fine. Currently I''m reading the file like this:

void Idle()
{
	static double dEventTime = 0;
	unsigned char ucEventType;
	short int mouseX = 0;
	short int mouseY = 0;

	GLint viewport[4];
	GLdouble mvmatrix[16], projmatrix[16];
	GLint realy; // OpenGL y coordinate position 

	GLdouble wx, wy, wz; // returned world x, y, z coords 


	if (g_bIsPlaying && ((g_cTimer->ElapsedTime()/* - g_dTimeOfLastEvent*/) >= dEventTime)) {
		//int filePos = g_TraceFile.tellg();

		g_dTimeOfLastEvent = g_cTimer->ElapsedTime();
		g_TraceFile.read((char*)&ucEventType, sizeof(unsigned char));
		g_TraceFile.read((char*)&dEventTime, sizeof(double));
		//filePos = g_TraceFile.tellg();

		switch (ucEventType) {
		case gc_ucEC_MouseMove:
			g_TraceFile.read((char*)&mouseX, sizeof(short int));
			g_TraceFile.read((char*)&mouseY, sizeof(short int));
			//filePos = g_TraceFile.tellg();

			glGetIntegerv (GL_VIEWPORT, viewport);
			glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
			glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
			// note viewport[3] is height of window in pixels 

			realy = viewport[3] - (GLint) mouseY - 1;
			gluUnProject ((GLdouble) mouseX, (GLdouble) realy, 0.0,
			mvmatrix, projmatrix, viewport, &wx, &wy, &wz);
			g_fFingerX = wx;
			g_fFingerY = wy;
			g_cTraceLine.push_back(wx);
			g_cTraceLine.push_back(wy);
			// mouse moved

			break;
		case gc_ucEC_MouseButton:
			g_TraceFile.read((char*)&g_bMouseButtonDown, sizeof(bool));
			// button changed

			break;
		}
	}
	if (g_bMouseButtonDown && g_cBalls[g_cBalls.size() - 1].Clicked(g_fFingerX, g_fFingerY)) {
		g_bCursorIsMoving = true;
	}
	if (!g_bMouseButtonDown) {
		g_bCursorIsMoving = false;
	}
	if (g_bCursorIsMoving) {
		g_cBalls[g_cBalls.size() - 1].MoveBall(g_fFingerX, g_fFingerY);
	}
}
What I''d like to do is the same thing only, read from memory, not from a file. Are sstreams any good? Thank you for your time.
Advertisement
Yeah, you'll want to use streams. As a matter of fact, you're already using a stream to read from the file. What you'll do is copy the whole file to memory, and wrap that memory into a stream. Then your code won't really have to change at all!
// Copy file to stringifstream fileStream("filename.dat");string fileData;fileData.assign( istreambuf_iterator<char>(fileStream),                 istreambuf_iterator<char>() );fileStream.close();//// Create bufferstreambuf streamBuffer;streamBuffer.sputn(fileData.c_str(), fileData.length());//// Create the streamistream dataStream(&streamBuffer);

And that's one way you could do it. Just use dataStream in place of what you use now. Although I'm sure someone more familiar with streams, buffers, and iterators will show me up with a better way of doing it. As a matter of fact, I'm sure they will

EDIT: Almost forgot, make sure the stream buffer has at least the same lifetime as the stream, otherwise you'll have problems.

[edited by - Zipster on June 10, 2004 1:43:31 PM]
That will probably work, but if all you''re reading is raw binary data (no formatting or operator >>, etc.), then there''s a faster way to do it:
std::ifstream g_TraceFile; // File streamstd::vector<char> g_TraceFileData; // File dataint g_TraceFileDataSize; // Data sizechar *g_pNextTraceFileItem; // Pointer to next item to be read from memory ... g_TraceFile.open("foo.bar");g_TraceFile.seekg(0, std::ios::end); // Go to end of fileg_TraceFileDataSize = g_TraceFile.tellg(); // Count how many bytes g_TraceFileData.resize(g_TraceFileDataSize); // Allocate memory g_TraceFile.seekg(0, std::ios::beg); // Go back to the beginning of the fileg_TraceFile.read(&g_TraceFileData[0], g_TraceFileDataSize); // Read in all the datag_pNextTraceFileItem = &g_TraceFileData[0]; // Set pointer to first byte ... assert(g_pNextTraceFileItem - &g_TraceFileData[0] < g_TraceFileDataSize - 9); // Make sure we have more data; you can get rid of this if you want ucEventType = *(unsigned char *)g_pNextTraceFileItem; g_pNextTraceFileItem += sizeof(unsigned char); // Read an unsigned chardEventTime = *(double *)g_pNextTraceFileItem; g_pNextTraceFileItem += sizeof(double); // Read a double
Here's another way that I think is closer to what the original
poster was asking:

struct MyData {
float x, y, z;
};

MyData data;

// Open up binary data file for reading
FILE* in = fopen("data.bin", "rb");
fread(&data, sizeof(MyData), 1, in);

There's a few things you need to be carful about here though:
(1) This won't work if the structure you're reading contains
pointers.
(2) The structure can't contain a v-table. In other words,
the object should't have any virtual functions.

[edited by - d_emmanuel on June 10, 2004 5:21:57 PM]
To add to what d_emmanuel suggested - if you take that approach, make sure that your structs are packed. If you create a struct with an unsigned char and a double, it is likely that the struct size will be 12 (not 9, as some might expect), because things ged padded to multiples of 4 bytes. To ensure that your structs are packed, do the following:

#pragma pack(push, 1) struct MyData{	unsigned char ucEventType;	double dEventTime;}; #pragma pack(pop)


The compiler keeps a stack of the current packing state, so the above code makes sure that the packing state will be the same afterwards as it was before.

This topic is closed to new replies.

Advertisement