I wrote a function, using the C header stdio.h that returns content of a file (text or html). Can anyone please go through it and suggest if I have done the memory management efficiently. I shall be so pleased to hear better suggestions that I can improve my codes.
char *getFileContent(const char *filePath)
{
//Prepare read file
FILE *pReadFile;
long bufferReadSize;
char *bufferReadFileHtml;
size_t readFileSize;
char readFilePath[50];
sprintf_s(readFilePath, "%s", filePath);
pReadFile = fopen (readFilePath, "rb");
if (pReadFile != NULL)
{
// Get file size.
fseek (pReadFile , 0 , SEEK_END);
bufferReadSize = ftell (pReadFile);
rewind (pReadFile);
// Allocate RAM to contain the whole file:
bufferReadFileHtml = (char*) malloc (sizeof(char) * bufferReadSize);
if (bufferReadFileHtml != NULL)
{
// Copy the file into the buffer:
readFileSize = fread (bufferReadFileHtml, sizeof(char), bufferReadSize, pReadFile);
if (readFileSize == bufferReadSize)
{
return bufferReadFileHtml;
} else {
char errorBuffer[50];
sprintf_s(errorBuffer, "Error! Buffer overflow for file: %s", readFilePath);
}
} else {
char errorBuffer[50];
sprintf_s(errorBuffer, "Error! Insufficient RAM for file: %s", readFilePath);
}
fclose (pReadFile);
free (bufferReadFileHtml);
} else {
char errorBuffer[50];
sprintf_s(errorBuffer, "Error! Unable to open file: %s", readFilePath);
}
}
This looks like a C program, not a C++ program. While it will compile using most C++ compilers, it doesn't take advantage of any C++ features (e.g. new/new[], delete/delete[], explicit casting, stream operators, strings, nullptr etc.)
Your code almost looks like a safe C function, although I think sprintf_s is a Microsoft-only function, and thus probably won't compile using GCC, Clang, Intel, etc. as it isn't a part of the standard.
Your function should also return a value at all times. Turn compiler warnings on to catch these kinds of things; they make debugging a lot easier :)
There's not much to be said without knowing how you will be using the buffer you've created. Here are some possible considerations:
1) When you are reading the file into a buffer, your processor is doing nothing else for this program. It might be better to read and begin analyzing the already read portion in parallel.
2) If you need really fast-efficient-low memory file IO, consider converting your program to a state machine and forget about the buffer altogether.
3) If you don't really have a very demanding application, you are killing yourself by writing in C. C#, python, etc-- almost any other language has better string manipulation libraries.
Btw, you should use snprintf for portability and safety, as others have pointed out.
Related
i have a text file which contains authors and books lists, i need to load it to my program, here is the code of the method which should load it:
void Loader::loadFile(const char* path)
{
FILE* file = fopen(path, "r");
char* bufferString;
while (feof(file) != 1) {
fgets(bufferString, 1000, file);
printf("%s", bufferString);
}
}
I use it in my main file:
int main(int argc, char** argv) {
Loader* loader = new Loader();
loader->loadFile("/home/terayon/prog/parser/data.txt");
return 0;
}
And I get data.txt file is not completely printed.
What I should do to get data completed?
fgets reads into the memory pointed to by the pointer passed as first parameter, bufferString on your case.
But your bufferString is an uninitialised pointer (leading to undefined behaviour):
char * bufferString;
// not initialised,
// and definitely not pointing to valid memory
So you need to provide some memory to read into, e.g by making it an array:
char bufferString[1000];
// that's a bit large to store on the stack
As a side note: Your code is not idiomatic C++. You're using the IO functions provided by the C standard library, which is possible, but using the facilities of the C++ STL would be more appropriate.
You have undefined behavior, you have a pointer bufferString but you never actually make int point anywhere. Since it's not initialized its value will be indeterminate and will seem to be random, meaning you will write to unallocated memory in the fgets call.
It's easy to solve though, declare it as an array, and use the array size when calling fgets:
char bufferString[500];
...
fgets(bufferString, sizeof(bufferString), file);
Besides the problem detailed above, you should not do while(!feof(file)), it will not work as you expect it to. The reason is that the EOF flag is not set until you try to read from beyond the file, leading the loop to iterate once to many.
You should instead do e.g. while (fgets(...) != NULL)
The code you have is not very C++-ish, instead it's using the old C functions for file handling. Instead I suggest you read more about the C++ standard I/O library and std::string which is a auto-expanding string class that won't have the limits of C arrays, and won't suffer from potential buffer overflows in the same way.
The code could then look something like this
std::ifstream input_file(path);
std::string input_buffer;
while (std::getline(input_file, input_buffer))
std::cout << input_buffer << '\n';
I'm profiling code of a game I wrote and I'm wondering how it is possible that the following snippet causes an heap increase of 4kb (I'm profiling with Heapshot Analysis of Xcode) every time it is executed:
u8 WorldManager::versionOfMap(FILE *file)
{
char magic[4];
u8 version;
fread(magic, 4, 1, file); <-- this is the line
fread(&version,1,1,file);
fseek(file, 0, SEEK_SET);
return version;
}
According to the profiler the highlighted line allocates 4.00Kb of memory with a malloc every time the function is called, memory which is never released. This thing seems to happen with other calls to fread around the code, but this was the most eclatant one.
Is there anything trivial I'm missing? Is it something internal I shouldn't care about?
Just as a note: I'm profiling it on an iPhone and it's compiled as release (-O2).
If what you're describing is really happening and your code has no bugs elsewhere, it is a bug in the implementation, I think.
More likely I think, is the possibility that you don't close the file. Stdio streams use buffering by default if the device is non-interactive, and the buffer is allocated either at the time the file is opened or when I/O is performed. While only one buffer should be allocated, you can certainly leak the buffer by forgetting to close the file. But certainly, closing the file should free the buffer. Don't forget to check the value returned by fclose.
Supposing for the sake of argument that you are correctly closing the file there are a couple of other nits in your code which won't be causing this problem but I'll mention anyway.
First your fread call reads an object having one member of size 4. You actually have an object having 4 members of size 1. In other words the numeric arguments to fread are swapped. This makes a difference only in the meaning of the return value (important in the case of a partial read).
Second, while your first call to fread correctly hard-codes the size of char as 1 (in C, that is the definition of 'size'), it's probably better stylistically to use sizeof(u8) in the second call to fread.
If the idea that this really is a memory leak is a correct interpretation (and there aren't any bugs elsewhere) then you may be able to work around the problem by turning off the stdio buffering for this particular file:
bool WorldManager::versionOfMap(FILE *file, bool *is_first_file_io, u8 *version)
{
char magic[4];
bool ok = false;
if (*is_first_file_io)
{
// we ignore failure of this call
setvbuf(file, NULL, _IONBF, 0);
*is_first_file_io = false;
}
if (sizeof(magic) == fread(magic, 1, sizeof(magic), file)
&& 1 == fread(version, sizeof(*version), 1, file))
{
ok = true;
}
if (-1 == fseek(file, 0L, SEEK_SET))
{
return false;
}
else
{
return ok && 0 == memcmp(magic, EXPECTED_MAGIC, sizeof(magic));
}
}
Even if we're going with the hypothesis that this really is a bug, and the leak is real, it is well worth condensing your code to the smallest possible example that still demonstrates the problem. If doing that reveals the true bug, you win. Otherwise, you will need the minimal example to report the bug in the implementation.
For some reasons I need a file pointer (FILE*) that points to nothing. It means I can pass it to fprintf function and fprintf ignore the file pointer.
for example:
void my_function()
{
FILE* nothing = NULL; // NULL is not correct.
int n = fprintf(nothing, "PI = %f", 3.14); // Note: When nothing = NULL, error ocured.
}
Is there a file pointer (FILE*) that points to nothing?
To solve your actual problem (stated in comments), use snprintf instead of printf, provided that it's available in your C++ implementation (which is not guaranteed in C++03). Pass in a null pointer for the buffer and 0 for the size. Nothing is written, but the return value is the length of the formatted string (excluding nul terminator).
[Edit: oops, I forgot that snprintf on Windows doesn't conform to C99. It returns an error if truncation occurs, not the required length. I don't know what they're going to do about the fact that C++0x requires C99-conforming snprintf.]
To answer your question, you can fopen /dev/null on UNIX-like systems or nul on Windows. Writes to the resulting FILE* have no effect. However, there is no portable null-device.
No.
Your code will cause a run-time exception. You can use /dev/null for example, if you're running on an OS that supports it, but there's nothing like that built-in in C++.
why not wrapping the fprintf method call with an if (NULL != nothing) statement?
Although as NirMH said, you can enclose it in if (nothing != NULL), there is another way. You can open a file in read mode (with "r") and when you send it to fprintf, the write is ignored (Edit: as discussed in the comments, remember to set n=0 if it was negative as returned by fprintf).
I personally suggest the first method though. The only reason I might do the second is if I can't change the function.
If you don't care if your code is system dependent, you can use /dev/null in linux, nul in windows etc,
You are making a design mistake.
Obviously, what you want to know is the number of chars needed to write your number.
You are using _*printf_ for that, which is a good idea. You just want to compute the number of chars "written", hence needed. But you don't want anything to be displayed, so you pricked fprintf instead of just printf. But fprintf doesn't work without a FILE to write in...
Like Steve said, you should rather use snprintf(), which writes into a string in memory.
As steve says, snprintf provided with a NULL string should work as intended except on windows. Then on windows, just provide it with an temporary string which you'll discard afterward.
size_t computeNumCharsNeededToPrintMyStuff(double d)
{
size_t ret = 0;
size_t maxBuffSize = 100; // should be plenty enough
char *tmpBuff = new char[maxBuffSize ];
ret = snprintf(tmpBuff, maxBuffSize, "PI = %f", d);
delete[] tmpBuff;
return ret;
}
Then you just call :
n = computeNumCharsNeededToPrintMyStuff(3.14)
(This func could be enhanced to compute the size needed to display anything but I'd rather keep it simple for now.)
Do you mean something like this?
#include <stdio.h>
int main()
{
FILE *fdevnull = fopen("/dev/null", "w");
printf("printf length=%d\n", fprintf(fdevnull, "Hello World '%s'=%f\n", "PI", 3.14159));
fclose(fdevnull);
return 0;
}
What is the fastest way to write an array of unsigned short values into a file, and then what would be the fastest way to read these in another application?
Also this would be on an apple machine running snow leopard.
If you don't worry about cross-platform compatibility, just write them all out in binary:
int save_shorts(const unsigned short* array, size_t num_shorts)
{
int ok = 0;
FILE* out = fopen("numbers.bin", "wb");
if (out != NULL)
{
ok = fwrite(array, num_shorts * sizeof *array, 1, out) == 1;
fclose(out);
}
return ok;
}
Reading them back in is very similiar but with fread(), of course. You could probably use C++ (binary) streams too, but this is simple enough.
There are about a gazillion approaches to this, starting from simple file I/O to compressing the data in memory under whatever format (zlib?) and then writing it to the file. It's up to you.
You can't go faster than mapping the file into memory and writing to that memory. This avoids stdio buffering and copying data between user-space and kernel.
I'm currently unpacking one of blizzard's .mpq file for reading.
For accessing the unpacked char buffer, I'm using a boost::interprocess::stream::memorybuffer.
Because .mpq files have a chunked structure always beginning with a version header (usually 12 bytes, see http://wiki.devklog.net/index.php?title=The_MoPaQ_Archive_Format#2.2_Archive_Header), the char* array representation seems to truncate at the first \0, even if the filesize (something about 1.6mb) remains constant and (probably) always allocated.
The result is a streambuffer with an effective length of 4 ('REVM' and byte nr.5 is \0). When attempting to read further, an exception is thrown. Here an example:
// (somewhere in the code)
{
MPQFile curAdt(FilePath);
size_t size = curAdt.getSize(); // roughly 1.6 mb
bufferstream memorybuf((char*)curAdt.getBuffer(), curAdt.getSize());
// bufferstream.m_buf.m_buffer is now 'REVM\0' (Debugger says so),
// but internal length field still at 1.6 mb
}
//////////////////////////////////////////////////////////////////////////////
// wrapper around a file oof the mpq_archive of libmpq
MPQFile::MPQFile(const char* filename) // I apologize my naming inconsistent convention :P
{
for(ArchiveSet::iterator i=gOpenArchives.begin(); i!=gOpenArchives.end();++i)
{
// gOpenArchives points to MPQArchive, wrapper around the mpq_archive, has mpq_archive * mpq_a as member
mpq_archive &mpq_a = (*i)->mpq_a;
// if file exists in that archive, tested via hash table in file, not important here, scroll down if you want
mpq_hash hash = (*i)->GetHashEntry(filename);
uint32 blockindex = hash.blockindex;
if ((blockindex == 0xFFFFFFFF) || (blockindex == 0)) {
continue; //file not found
}
uint32 fileno = blockindex;
// Found!
size = libmpq_file_info(&mpq_a, LIBMPQ_FILE_UNCOMPRESSED_SIZE, fileno);
// HACK: in patch.mpq some files don't want to open and give 1 for filesize
if (size<=1) {
eof = true;
buffer = 0;
return;
}
buffer = new char[size]; // note: size is 1.6 mb at this time
// Now here comes the tricky part... if I step over the libmpq_file_getdata
// function, I'll get my truncated char array, which I absolutely don't want^^
libmpq_file_getdata(&mpq_a, hash, fileno, (unsigned char*)buffer);
return;
}
}
Maybe someone could help me. I'm really new to STL and boost programming and also inexperienced in C++ programming anyways :P Hope to get a convenient answer (plz not suggest to rewrite libmpq and the underlying zlib architecture^^).
The MPQFile class and the underlying uncompress methods are acutally taken from a working project, so the mistake is either somewhere in the use of the buffer with the streambuffer class or something internal with char array arithmetic I haven't a clue of.
By the way, what is the difference between using signed/unsigned chars as data buffers? Has it anything to do with my problem (you might see, that in the code randomly char* unsigned char* is taken as function arguments)
If you need more infos, feel free to ask :)
How are you determining that your char* array is being 'truncated' as you call it? If you're printing it or viewing it in a debugger it will look truncated because it will be treated like a string, which is terminated by \0. The data in 'buffer' however (assuming libmpq_file_getdata() does what it's supposed to do) will contain the whole file or data chunk or whatever.
Sorry, messed up a bit with these terms (not memorybuffer actually, streambuffer is meant as in the code)
Yeah you where right... I had a mistake in my exception handling. Right after that first bit of code comes this:
// check if the file has been open
//if (!mpf.is_open())
pair<char*, size_t> temp = memorybuf.buffer();
if(temp.first)
throw AdtException(ADT_PARSEERR_EFILE);//Can't open the File
notice the missing ! before temp.first . I was surprized by the exception thrown, looked at the streambuffer .. internal buffer at was confused of its length (C# background :P).
Sorry for that, it's working as expected now.