Search code examples
c++linuxfilememory-mapped-files

Grow a file for further writing to using boost::mapped_region?


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?


Solution

  • 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();
    }
    

    http://coliru.stacked-crooked.com/a/ae224032dd036639