memory mapped filesについて

最近なんか抽象的な事しか書いてない気がしてきたのでboostの奴でちょろっと。

boost.interprocessってあんまりTwitter上でネタにならないような気がしますね*1

boost.interprocessにはmemory mapped filesという機能があります。簡単に言うとファイルに対して仮想的にアドレスを与えてメモリにアクセスする場合と同等な機能を提供してくれます(仮想的なアドレス、とありますが実際にはこの機能が勝手にメモリ上にファイルのデータをロードします)。
使い方はfile_mappingでマッピングを行って、mapped_regionで読み出しができるように"ビュー"を作成する。後はビューからアドレスとそのサイズを取得してメモリと同じようにアクセスするだけ、の簡単なものです。

#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>

namespace bip = boost::interprocess;

int main()
{
    bip::file_mapping map( "tmp.dat", bip::read_only ); // ファイルを読み込み専用でマッピング
    bip::mapped_region view( map, bip::read_only );     // ビューを作成して...

    // メモリと同じようにアクセスできる
    // 先頭4バイト読み出し
    if( view.get_size() >= 4 ) {
        char buf[4];
        memcpy( buf, view.get_address(), 4 );

        // 読みだしたデータで何かしら処理...
    }
}

使い方は非常に簡単ですね。
boost.interprocessにとっては提供してる一機能、一部でしかないわけですが、僕は強力で危ない機能だと思ってます。
何せファイルをメモリと大差なく*2読み出し、書き出しができるわけですから、メモリアクセスと同レベルの危険性があります。
こういうのが絡んでくるとファイルに構造体をコピーするときのアラインメントがやいのやいのとなって面倒だったりする訳ですが今回は割愛。
僕が今日言いたいのはこれを丁寧に使ってPCゲームでよくあるアーカイバ機能とか作れるんじゃないかなーという事です。
Direct3Dとかだとメモリ上イメージからリソースを構築できる関数とかあ・・・りましたよね?(OpenGLはちょっと分かりません, OpenALはあったと思います)メモリ上イメージ、ということは"メモリと同じようにアクセスできる"ようになってれば、この辺の関数も使えるという事です。まさにmemory mapped filesはメモリと同じようにアクセスできる機能で、このために作られた機能といっても過言ではない(多分過言)と思いますので、ちょっと考えてみたいと思ってたりします。まあ、結構危険な機能な気もするので丁重に扱いましょう。
boost.interprocessにはこれをラップした上位機能としてmanaged mapped filesというのもあるそうなので、そっちも調べてみたいと思ってます。

ファイルコピーをしてみるようなやつ書いてみた

とは言ってもいろいろ考えてないので危ない書き方になってるかもしれませんが。

#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/filesystem/v2/convenience.hpp>
#include <fstream>
#include <string>
#include <iostream>

void create_buffile( std::string const& FilePath, std::string const& CopyName )
{
    std::filebuf fbuf;
    fbuf.open( CopyName, std::ios_base::in | std::ios_base::out 
                       | std::ios_base::trunc | std::ios_base::binary );
    fbuf.pubseekoff( boost::filesystem::file_size(FilePath) - 1, std::ios_base::beg );
    fbuf.sputc(0);
}

int main()
{
    namespace bip = boost::interprocess;
	
    const std::string FilePath( "test.ogg" );
    const std::string CopyName( "copy.ogg" );
    create_buffile( FilePath, CopyName );

    try {
        bip::file_mapping fmsrc( FilePath.c_str(), bip::read_only );
        bip::file_mapping fmdst( CopyName.c_str(), bip::read_write );
        bip::mapped_region mapsrc( fmsrc, bip::read_only );
        bip::mapped_region mapdst( fmdst, bip::read_write );

        BOOST_ASSERT( mapsrc.get_size() == mapdst.get_size() );
        memcpy( mapdst.get_address(), mapsrc.get_address(), mapsrc.get_size() );

    } catch( std::exception e ) {
        std::cout << e.what() << std::endl;
    }
}

*1:boost.interprocessのunique_ptrぐらいな気が

*2:もちろん、アクセス速度は違うと思いますが