I need to create and write to a memory mapped file. At times it is necessary to grow the file.
I have created the following small test which I create a file, map it using boost::mapped_region
and write into it.
This all works as expected:
#include <fstream>
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
namespace bip = boost::interprocess;
void createFile(const char* fn, std::uint64_t num)
{
std::filebuf fbuf;
fbuf.open(fn, std::ios_base::out|std::ios_base::binary|std::ios_base::trunc);
const std::uint64_t size = sizeof(std::uint64_t) * (num + 1);
fbuf.pubseekoff(size - 1, std::ios_base::beg);
fbuf.sputc(0);
fbuf.close();
}
void writeToFile(const char* fn, std::uint64_t pos, std::uint64_t val)
{
bip::file_mapping fm(fn, bip::read_write);
bip::mapped_region rg(fm, bip::read_write);
std::uint64_t* p = reinterpret_cast<std::uint64_t*>(rg.get_address());
*p = std::max(*p, pos); // store max num values
*(p + pos) = val; // write value into position
}
int main ()
{
const char* fn = "/tmp/test.dat";
createFile(fn, 3);
writeToFile(fn, 1, 0x1111111111111111);
writeToFile(fn, 2, 0x2222222222222222);
writeToFile(fn, 3, 0x3333333333333333);
return 0;
}
Running this program produces an output file as expected, and when I dump its contents I can see the values correctly written therein:
$ xxd -p /tmp/test.dat
030000000000000011111111111111112222222222222222333333333333
3333
Now, however, I want to resize the file so that I can write additional data at the end.
I add the following function, growFile
(which uses ios_base::app
as suggested by timrau below)
void growFile(const char* fn, std::uint64_t num)
{
std::filebuf fbuf;
fbuf.open(fn, std::ios_base::out|std::ios_base::binary|std::ios_base::app);
const std::uint64_t size = sizeof(std::uint64_t) * (num + 1);
fbuf.pubseekoff(size - 1, std::ios_base::beg);
fbuf.sputc(0);
fbuf.close();
}
I now add further values after growing the file:
int main ()
{
const char* fn = "/tmp/test.dat";
createFile(fn, 3);
writeToFile(fn, 1, 0x1111111111111111);
writeToFile(fn, 2, 0x2222222222222222);
writeToFile(fn, 3, 0x3333333333333333);
growFile(fn, 6);
writeToFile(fn, 4, 0x4444444444444444);
writeToFile(fn, 5, 0x5555555555555555);
writeToFile(fn, 6, 0x6666666666666666);
return 0;
}
When I dump the file it's missing most of the new values.
$ xxd -p /tmp/test.dat
060000000000000011111111111111112222222222222222333333333333
333344
Note that if I don't grow the file, but just create it with sufficient space initially, it works as expected:
int main ()
{
const char* fn = "/tmp/test.dat";
createFile(fn, 6);
writeToFile(fn, 1, 0x1111111111111111);
writeToFile(fn, 2, 0x2222222222222222);
writeToFile(fn, 3, 0x3333333333333333);
writeToFile(fn, 4, 0x4444444444444444);
writeToFile(fn, 5, 0x5555555555555555);
writeToFile(fn, 6, 0x6666666666666666);
return 0;
}
When I dump the file all the values are there:
$ xxd -p /tmp/test.dat
060000000000000011111111111111112222222222222222333333333333
3333444444444444444455555555555555556666666666666666
How can I grow my file after creation so that I can write further that its initial size?
I had another play on coliru.
You have to open the file for read/write - equivalent to ::fopen(..., "r+")
These options all work:
void resizeFile(const char* fn)
{
constexpr auto offset = sizeof(std::uint64_t) * 6 - 1;
/*
FILE* fp = ::fopen(fn, "r+");
::fseek(fp, offset, SEEK_SET);
::fputc(0, fp);
::fclose(fp);
*/
/*
std::fstream f;
f.exceptions(std::ios::failbit | std::ios::badbit);
f.open(fn, std::ios_base::in | std::ios_base::out | std::ios_base::binary);
f.seekp(offset, std::ios_base::beg);
f.put(0);
f.flush();
*/
std::filebuf fbuf;
fbuf.open(fn, std::ios_base::in | std::ios_base::out | std::ios_base::binary);
fbuf.pubseekoff(offset, std::ios_base::beg);
fbuf.sputc(0);
fbuf.close();
}