You can accomplish this by moving one byte at a time, as show below. However, you'll get much better performance if you allow for a larger buffer (moving 4096 bytes at a time). We obviously use some stack memory, but it makes no allocations based on the size of your prefix or the size of your file, so we can call this 'in place'.
void inPlaceTruncate(
char const * const filename,
int shift)
{
FILE * f;
if ((f = fopen(filename, "r+")) == NULL) {
// handle error
}
// go to the end
if (fseek(f, 0, SEEK_END) != 0) {
// handle error
}
// get current file size
long int const oldFileLen = ftell(f);
if (oldFileLen < 0) {
// handle error
} else if (oldFileLen < shift) {
// make the file empty
shift = oldFileLen;
}
// go back to the beginning
if (fseek(f, 0, SEEK_SET) != 0) {
// handle error
}
// move file
char buffer;
for (long int pos = 0; pos < oldFileLen-shift; ++pos) {
// slow to be repeatedly fseeking...
if (fseek(f, pos+shift, SEEK_SET) != 0) {
// handle error
}
if (fread(&buffer, sizeof(buffer), 1, f) != 1) {
// handle error
}
if (fseek(f, pos, SEEK_SET) != 0) {
// handle error
}
if (fwrite(&buffer, sizeof(buffer), 1, f) != 1) {
// handle error
}
}
// shrink file -- in a rather unpleasent way
#ifdef WIN32
if (_chsize(fileno(f), oldFileLen-shift) != 0) {
// handle error
}
#else
// we're assuming if its not windows, it's at least posix compliant.
if (ftruncate(fileno(f), oldFileLen-shift) != 0) {
// handle error
}
#endif
fclose(f);
}
A related post for file shrinking.
Edited to actually answer the OP's question.
Edited to note places for error handling.
Also, as pointed in the comments, this will only handle files and shifts of less than 2GB. In order to handle larger files and work around the fseek()
/ftell()
/ftruncate()
32-bit limitation (on Windows you can use _chsize_s), you would need to determine the filesize in a loop using relative offsets, and make multiple calls to ftruncate()
.